summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/dsp/fsl,dsp.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/apple,mca.yaml131
-rw-r--r--Documentation/devicetree/bindings/sound/audio-graph-port.yaml19
-rw-r--r--Documentation/devicetree/bindings/sound/audio-graph.yaml9
-rw-r--r--Documentation/devicetree/bindings/sound/dai-params.yaml40
-rwxr-xr-xDocumentation/devicetree/bindings/sound/everest,es8326.yaml116
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,sai.yaml216
-rw-r--r--Documentation/devicetree/bindings/sound/fsl-sai.txt95
-rw-r--r--Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-card.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,sm8250.yaml6
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.yaml14
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,snow.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/st,stm32-sai.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/ti,src4xxx.yaml48
-rw-r--r--Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml5
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/soundwire/bus.c32
-rw-r--r--drivers/soundwire/cadence_master.c8
-rw-r--r--drivers/soundwire/cadence_master.h2
-rw-r--r--drivers/soundwire/intel.c1
-rw-r--r--include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h18
-rw-r--r--include/linux/soundwire/sdw.h5
-rw-r--r--include/sound/simple_card_utils.h1
-rw-r--r--include/sound/soc-acpi-intel-match.h3
-rw-r--r--include/sound/soc.h20
-rw-r--r--include/sound/sof.h2
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/amd/acp/acp-i2s.c80
-rw-r--r--sound/soc/amd/acp/acp-pci.c17
-rw-r--r--sound/soc/amd/acp/acp-platform.c44
-rw-r--r--sound/soc/amd/acp/amd.h22
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c28
-rw-r--r--sound/soc/apple/Kconfig9
-rw-r--r--sound/soc/apple/Makefile3
-rw-r--r--sound/soc/apple/mca.c1167
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c5
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c2
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/codecs/Kconfig26
-rw-r--r--sound/soc/codecs/Makefile6
-rw-r--r--sound/soc/codecs/cs42l42.c24
-rw-r--r--sound/soc/codecs/cs42l42.h13
-rw-r--r--sound/soc/codecs/cs43130.c11
-rw-r--r--sound/soc/codecs/es8316.c22
-rwxr-xr-xsound/soc/codecs/es8326.c905
-rwxr-xr-xsound/soc/codecs/es8326.h182
-rw-r--r--sound/soc/codecs/max98088.c7
-rw-r--r--sound/soc/codecs/max98373-sdw.c2
-rw-r--r--sound/soc/codecs/mt6359-accdet.c6
-rw-r--r--sound/soc/codecs/rt1308-sdw.c2
-rw-r--r--sound/soc/codecs/rt1316-sdw.c2
-rw-r--r--sound/soc/codecs/rt5640.c5
-rw-r--r--sound/soc/codecs/rt5682-sdw.c2
-rw-r--r--sound/soc/codecs/rt700-sdw.c2
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt715-sdw.c2
-rw-r--r--sound/soc/codecs/sigmadsp.c4
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c47
-rw-r--r--sound/soc/codecs/src4xxx.c518
-rw-r--r--sound/soc/codecs/src4xxx.h113
-rw-r--r--sound/soc/codecs/tas2764.c181
-rw-r--r--sound/soc/codecs/tas2764.h23
-rw-r--r--sound/soc/codecs/tas2770.c98
-rw-r--r--sound/soc/codecs/tas2770.h5
-rw-r--r--sound/soc/codecs/tlv320adcx140.c59
-rw-r--r--sound/soc/codecs/tlv320adcx140.h3
-rw-r--r--sound/soc/codecs/tlv320aic26.c2
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c9
-rw-r--r--sound/soc/codecs/uda134x.c2
-rw-r--r--sound/soc/codecs/wcd9335.c10
-rw-r--r--sound/soc/codecs/wcd938x.c1
-rw-r--r--sound/soc/fsl/fsl_sai.c57
-rw-r--r--sound/soc/fsl/fsl_sai.h3
-rw-r--r--sound/soc/fsl/imx-rpmsg.c29
-rw-r--r--sound/soc/generic/simple-card-utils.c34
-rw-r--r--sound/soc/intel/Kconfig2
-rw-r--r--sound/soc/intel/atom/sst/sst.c8
-rw-r--r--sound/soc/intel/avs/pcm.c4
-rw-r--r--sound/soc/intel/boards/Makefile8
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c92
-rw-r--r--sound/soc/intel/boards/sof_es8336.c4
-rw-r--r--sound/soc/intel/catpt/device.c33
-rw-r--r--sound/soc/intel/catpt/sysfs.c6
-rw-r--r--sound/soc/intel/common/Makefile2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c15
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c51
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c2
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c3
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-i2s.c14
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c170
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c170
-rw-r--r--sound/soc/qcom/qdsp6/q6prm-clocks.c9
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.h19
-rw-r--r--sound/soc/samsung/aries_wm8994.c8
-rw-r--r--sound/soc/sh/rz-ssi.c26
-rw-r--r--sound/soc/soc-ac97.c8
-rw-r--r--sound/soc/soc-core.c2
-rw-r--r--sound/soc/soc-dai.c2
-rw-r--r--sound/soc/soc-dapm.c9
-rw-r--r--sound/soc/soc-pcm.c8
-rw-r--r--sound/soc/soc-utils-test.c46
-rw-r--r--sound/soc/soc-utils.c23
-rw-r--r--sound/soc/sof/compress.c51
-rw-r--r--sound/soc/sof/debug.c6
-rw-r--r--sound/soc/sof/imx/Kconfig9
-rw-r--r--sound/soc/sof/imx/Makefile2
-rw-r--r--sound/soc/sof/imx/imx8ulp.c515
-rw-r--r--sound/soc/sof/intel/hda-dsp.c7
-rw-r--r--sound/soc/sof/intel/hda-loader.c10
-rw-r--r--sound/soc/sof/intel/hda.c2
-rw-r--r--sound/soc/sof/intel/hda.h6
-rw-r--r--sound/soc/sof/intel/pci-tgl.c62
-rw-r--r--sound/soc/sof/ipc3-topology.c2
-rw-r--r--sound/soc/sof/ipc4-loader.c11
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c58
-rw-r--r--sound/soc/sof/pcm.c8
-rw-r--r--sound/soc/sof/sof-audio.c1
-rw-r--r--sound/soc/sof/sof-of-dev.h7
-rw-r--r--sound/soc/sof/sof-priv.h7
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c6
-rw-r--r--sound/soc/ti/omap-mcbsp.c8
130 files changed, 5633 insertions, 530 deletions
diff --git a/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml b/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml
index e66ef2da7879..9af40da5688e 100644
--- a/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml
+++ b/Documentation/devicetree/bindings/dsp/fsl,dsp.yaml
@@ -20,6 +20,7 @@ properties:
- fsl,imx8qxp-dsp
- fsl,imx8qm-dsp
- fsl,imx8mp-dsp
+ - fsl,imx8ulp-dsp
- fsl,imx8qxp-hifi4
- fsl,imx8qm-hifi4
- fsl,imx8mp-hifi4
diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
index b4b35edcb493..5b8d59245f82 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
@@ -40,6 +40,7 @@ properties:
patternProperties:
"^dai-link-[0-9]+$":
type: object
+ additionalProperties: false
description: |-
dai-link child nodes:
Container for dai-link level properties and the CODEC sub-nodes.
@@ -63,6 +64,7 @@ patternProperties:
patternProperties:
"^codec-[0-9]+$":
type: object
+ additionalProperties: false
description: |-
Codecs:
dai-link representing backend links should have at least one subnode.
diff --git a/Documentation/devicetree/bindings/sound/apple,mca.yaml b/Documentation/devicetree/bindings/sound/apple,mca.yaml
new file mode 100644
index 000000000000..d5dc92b5b654
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/apple,mca.yaml
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/apple,mca.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Apple MCA I2S transceiver
+
+description: |
+ MCA is an I2S transceiver peripheral found on M1 and other Apple chips. It is
+ composed of a number of identical clusters which can operate independently
+ or in an interlinked fashion. Up to 6 clusters have been seen on an MCA.
+
+maintainers:
+ - Martin Povišer <povik+lin@cutebit.org>
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - apple,t6000-mca
+ - apple,t8103-mca
+ - const: apple,mca
+
+ reg:
+ items:
+ - description: Register region of the MCA clusters proper
+ - description: Register region of the DMA glue and its FIFOs
+
+ interrupts:
+ minItems: 4
+ maxItems: 6
+ description:
+ One interrupt per each cluster
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ dmas:
+ minItems: 16
+ maxItems: 24
+ description:
+ DMA channels corresponding to the SERDES units in the peripheral. They are
+ listed in groups of four per cluster, and within the group they are given
+ as associated to the TXA, RXA, TXB, RXB units.
+
+ dma-names:
+ minItems: 16
+ items:
+ - const: tx0a
+ - const: rx0a
+ - const: tx0b
+ - const: rx0b
+ - const: tx1a
+ - const: rx1a
+ - const: tx1b
+ - const: rx1b
+ - const: tx2a
+ - const: rx2a
+ - const: tx2b
+ - const: rx2b
+ - const: tx3a
+ - const: rx3a
+ - const: tx3b
+ - const: rx3b
+ - const: tx4a
+ - const: rx4a
+ - const: tx4b
+ - const: rx4b
+ - const: tx5a
+ - const: rx5a
+ - const: tx5b
+ - const: rx5b
+ description: |
+ Names for the DMA channels: 'tx'/'rx', then cluster number, then 'a'/'b'
+ based on the associated SERDES unit.
+
+ clocks:
+ minItems: 4
+ maxItems: 6
+ description:
+ Clusters' input reference clock.
+
+ resets:
+ maxItems: 1
+
+ power-domains:
+ minItems: 5
+ maxItems: 7
+ description:
+ First a general power domain for register access, then the power
+ domains of individual clusters for their operation.
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - dmas
+ - dma-names
+ - clocks
+ - power-domains
+ - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ mca: i2s@9b600000 {
+ compatible = "apple,t6000-mca", "apple,mca";
+ reg = <0x9b600000 0x10000>,
+ <0x9b200000 0x20000>;
+
+ clocks = <&nco 0>, <&nco 1>, <&nco 2>, <&nco 3>;
+ power-domains = <&ps_audio_p>, <&ps_mca0>, <&ps_mca1>,
+ <&ps_mca2>, <&ps_mca3>;
+ dmas = <&admac 0>, <&admac 1>, <&admac 2>, <&admac 3>,
+ <&admac 4>, <&admac 5>, <&admac 6>, <&admac 7>,
+ <&admac 8>, <&admac 9>, <&admac 10>, <&admac 11>,
+ <&admac 12>, <&admac 13>, <&admac 14>, <&admac 15>;
+ dma-names = "tx0a", "rx0a", "tx0b", "rx0b",
+ "tx1a", "rx1a", "tx1b", "rx1b",
+ "tx2a", "rx2a", "tx2b", "rx2b",
+ "tx3a", "rx3a", "tx3b", "rx3b";
+
+ #sound-dai-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
index 5c368674d11a..bc46a95ed840 100644
--- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
+++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
@@ -19,14 +19,17 @@ properties:
description: "device name prefix"
$ref: /schemas/types.yaml#/definitions/string
convert-rate:
- description: CPU to Codec rate convert.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
convert-channels:
- description: CPU to Codec rate channels.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+ convert-sample-format:
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+
patternProperties:
"^endpoint(@[0-9a-f]+)?":
$ref: /schemas/graph.yaml#/$defs/endpoint-base
+ unevaluatedProperties: false
+
properties:
mclk-fs:
description: |
@@ -65,11 +68,11 @@ patternProperties:
- msb
- lsb
convert-rate:
- description: CPU to Codec rate convert.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
convert-channels:
- description: CPU to Codec rate channels.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+ convert-sample-format:
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
dai-tdm-slot-width-map:
description: Mapping of sample widths to slot widths. For hardware
diff --git a/Documentation/devicetree/bindings/sound/audio-graph.yaml b/Documentation/devicetree/bindings/sound/audio-graph.yaml
index 4b46794e5153..aaa99c2deda0 100644
--- a/Documentation/devicetree/bindings/sound/audio-graph.yaml
+++ b/Documentation/devicetree/bindings/sound/audio-graph.yaml
@@ -27,11 +27,12 @@ properties:
description: User specified audio sound widgets.
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
convert-rate:
- description: CPU to Codec rate convert.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
convert-channels:
- description: CPU to Codec rate channels.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+ convert-sample-format:
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+
pa-gpios:
maxItems: 1
hp-det-gpio:
diff --git a/Documentation/devicetree/bindings/sound/dai-params.yaml b/Documentation/devicetree/bindings/sound/dai-params.yaml
new file mode 100644
index 000000000000..f5fb71f9b603
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/dai-params.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/dai-params.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Digital Audio Interface (DAI) Stream Parameters
+
+maintainers:
+ - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+select: false
+
+$defs:
+
+ dai-channels:
+ description: Number of audio channels used by DAI
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 1
+ maximum: 32
+
+ dai-sample-format:
+ description: Audio sample format used by DAI
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - s8
+ - s16_le
+ - s24_le
+ - s24_3le
+ - s32_le
+
+ dai-sample-rate:
+ description: Audio sample rate used by DAI
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 8000
+ maximum: 192000
+
+properties: {}
+
+additionalProperties: true
diff --git a/Documentation/devicetree/bindings/sound/everest,es8326.yaml b/Documentation/devicetree/bindings/sound/everest,es8326.yaml
new file mode 100755
index 000000000000..07781408e788
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/everest,es8326.yaml
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/everest,es8326.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Everest ES8326 audio CODEC
+
+maintainers:
+ - David Yang <yangxiaohua@everest-semi.com>
+
+properties:
+ compatible:
+ const: everest,es8326
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: clock for master clock (MCLK)
+
+ clock-names:
+ items:
+ - const: mclk
+
+ "#sound-dai-cells":
+ const: 0
+
+ everest,jack-pol:
+ $ref: /schemas/types.yaml#/definitions/uint8
+ description: |
+ just the value of reg 57. Bit(3) decides whether the jack polarity is inverted.
+ Bit(2) decides whether the button on the headset is inverted.
+ Bit(1)/(0) decides the mic properity to be OMTP/CTIA or auto.
+ minimum: 0x00
+ maximum: 0x0f
+ default: 0x0f
+
+ everest,mic1-src:
+ $ref: /schemas/types.yaml#/definitions/uint8
+ description:
+ the value of reg 2A when headset plugged.
+ minimum: 0x00
+ maximum: 0x77
+ default: 0x22
+
+ everest,mic2-src:
+ $ref: /schemas/types.yaml#/definitions/uint8
+ description:
+ the value of reg 2A when headset unplugged.
+ minimum: 0x00
+ maximum: 0x77
+ default: 0x44
+
+ everest,jack-detect-inverted:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ Defined to invert the jack detection.
+
+ everest,interrupt-src:
+ $ref: /schemas/types.yaml#/definitions/uint8
+ description: |
+ value of reg 0x58, Defines the interrupt source.
+ Bit(2) 1 means button press triggers irq, 0 means not.
+ Bit(3) 1 means PIN9 is the irq source for jack detection. When set to 0,
+ bias change on PIN9 do not triggers irq.
+ Bit(4) 1 means PIN27 is the irq source for jack detection.
+ Bit(5) 1 means PIN9 is the irq source after MIC detect.
+ Bit(6) 1 means PIN27 is the irq source after MIC detect.
+ minimum: 0
+ maximum: 0x3c
+ default: 0x08
+
+ everest,interrupt-clk:
+ $ref: /schemas/types.yaml#/definitions/uint8
+ description: |
+ value of reg 0x59, Defines the interrupt output behavior.
+ Bit(0-3) 0 means irq pulse equals 512*internal clock
+ 1 means irq pulse equals 1024*internal clock
+ 2 means ...
+ 7 means irq pulse equals 65536*internal clock
+ 8 means irq mutes PA
+ 9 means irq mutes PA and DAC output
+ Bit(4) 1 means we invert the interrupt output.
+ Bit(6) 1 means the chip do not detect jack type after button released.
+ 0 means the chip detect jack type again after button released.
+ minimum: 0
+ maximum: 0x7f
+ default: 0x45
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ es8326: codec@19 {
+ compatible = "everest,es8326";
+ reg = <0x19>;
+ clocks = <&clks 10>;
+ clock-names = "mclk";
+ #sound-dai-cells = <0>;
+ everest,mic1-src = [22];
+ everest,mic2-src = [44];
+ everest,jack-pol = [0e];
+ everest,interrupt-src = [08];
+ everest,interrupt-clk = [45];
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
new file mode 100644
index 000000000000..70c4111d59c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
@@ -0,0 +1,216 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/fsl,sai.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale Synchronous Audio Interface (SAI).
+
+maintainers:
+ - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+description: |
+ The SAI is based on I2S module that used communicating with audio codecs,
+ which provides a synchronous audio interface that supports fullduplex
+ serial interfaces with frame synchronization such as I2S, AC97, TDM, and
+ codec/DSP interfaces.
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - fsl,vf610-sai
+ - fsl,imx6sx-sai
+ - fsl,imx6ul-sai
+ - fsl,imx7ulp-sai
+ - fsl,imx8mq-sai
+ - fsl,imx8qm-sai
+ - fsl,imx8ulp-sai
+ - items:
+ - enum:
+ - fsl,imx8mm-sai
+ - fsl,imx8mn-sai
+ - fsl,imx8mp-sai
+ - const: fsl,imx8mq-sai
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: receive and transmit interrupt
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ maxItems: 2
+
+ clocks:
+ items:
+ - description: The ipg clock for register access
+ - description: master clock source 0 (obsoleted)
+ - description: master clock source 1
+ - description: master clock source 2
+ - description: master clock source 3
+ - description: PLL clock source for 8kHz series
+ - description: PLL clock source for 11kHz series
+ minItems: 4
+
+ clock-names:
+ oneOf:
+ - items:
+ - const: bus
+ - const: mclk0
+ - const: mclk1
+ - const: mclk2
+ - const: mclk3
+ - const: pll8k
+ - const: pll11k
+ minItems: 4
+ - items:
+ - const: bus
+ - const: mclk1
+ - const: mclk2
+ - const: mclk3
+ - const: pll8k
+ - const: pll11k
+ minItems: 4
+
+ lsb-first:
+ description: |
+ Configures whether the LSB or the MSB is transmitted
+ first for the fifo data. If this property is absent,
+ the MSB is transmitted first as default, or the LSB
+ is transmitted first.
+ type: boolean
+
+ big-endian:
+ description: |
+ required if all the SAI registers are big-endian rather than little-endian.
+ type: boolean
+
+ fsl,sai-synchronous-rx:
+ description: |
+ SAI will work in the synchronous mode (sync Tx with Rx) which means
+ both the transmitter and the receiver will send and receive data by
+ following receiver's bit clocks and frame sync clocks.
+ type: boolean
+
+ fsl,sai-asynchronous:
+ description: |
+ SAI will work in the asynchronous mode, which means both transmitter
+ and receiver will send and receive data by following their own bit clocks
+ and frame sync clocks separately.
+ If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
+ default synchronous mode (sync Rx with Tx) will be used, which means both
+ transmitter and receiver will send and receive data by following clocks
+ of transmitter.
+ type: boolean
+
+ fsl,dataline:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ description: |
+ Configure the dataline. It has 3 value for each configuration
+ maxItems: 16
+ items:
+ items:
+ - description: format Default(0), I2S(1) or PDM(2)
+ enum: [0, 1, 2]
+ - description: dataline mask for 'rx'
+ - description: dataline mask for 'tx'
+
+ fsl,sai-mclk-direction-output:
+ description: SAI will output the SAI MCLK clock.
+ type: boolean
+
+ fsl,shared-interrupt:
+ description: Interrupt is shared with other modules.
+ type: boolean
+
+ "#sound-dai-cells":
+ const: 0
+ description: optional, some dts node didn't add it.
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,vf610-sai
+ then:
+ properties:
+ dmas:
+ items:
+ - description: DMA controller phandle and request line for TX
+ - description: DMA controller phandle and request line for RX
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+ else:
+ properties:
+ dmas:
+ items:
+ - description: DMA controller phandle and request line for RX
+ - description: DMA controller phandle and request line for TX
+ dma-names:
+ items:
+ - const: rx
+ - const: tx
+ - if:
+ required:
+ - fsl,sai-asynchronous
+ then:
+ properties:
+ fsl,sai-synchronous-rx: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - dmas
+ - dma-names
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/vf610-clock.h>
+ sai2: sai@40031000 {
+ compatible = "fsl,vf610-sai";
+ reg = <0x40031000 0x1000>;
+ interrupts = <86 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sai2_1>;
+ clocks = <&clks VF610_CLK_PLATFORM_BUS>,
+ <&clks VF610_CLK_SAI2>,
+ <&clks 0>, <&clks 0>;
+ clock-names = "bus", "mclk1", "mclk2", "mclk3";
+ dma-names = "tx", "rx";
+ dmas = <&edma0 0 21>,
+ <&edma0 0 20>;
+ big-endian;
+ lsb-first;
+ };
+
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/imx8mm-clock.h>
+ sai1: sai@30010000 {
+ compatible = "fsl,imx8mm-sai", "fsl,imx8mq-sai";
+ reg = <0x30010000 0x10000>;
+ interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk IMX8MM_CLK_SAI1_IPG>,
+ <&clk IMX8MM_CLK_DUMMY>,
+ <&clk IMX8MM_CLK_SAI1_ROOT>,
+ <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>;
+ clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";
+ dmas = <&sdma2 0 2 0>, <&sdma2 1 2 0>;
+ dma-names = "rx", "tx";
+ fsl,dataline = <1 0xff 0xff 2 0xff 0x11>;
+ #sound-dai-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt
deleted file mode 100644
index fbdefc3fade7..000000000000
--- a/Documentation/devicetree/bindings/sound/fsl-sai.txt
+++ /dev/null
@@ -1,95 +0,0 @@
-Freescale Synchronous Audio Interface (SAI).
-
-The SAI is based on I2S module that used communicating with audio codecs,
-which provides a synchronous audio interface that supports fullduplex
-serial interfaces with frame synchronization such as I2S, AC97, TDM, and
-codec/DSP interfaces.
-
-Required properties:
-
- - compatible : Compatible list, contains "fsl,vf610-sai",
- "fsl,imx6sx-sai", "fsl,imx6ul-sai",
- "fsl,imx7ulp-sai", "fsl,imx8mq-sai",
- "fsl,imx8qm-sai", "fsl,imx8mm-sai",
- "fsl,imx8mn-sai", "fsl,imx8mp-sai", or
- "fsl,imx8ulp-sai".
-
- - reg : Offset and length of the register set for the device.
-
- - clocks : Must contain an entry for each entry in clock-names.
-
- - clock-names : Must include the "bus" for register access and
- "mclk1", "mclk2", "mclk3" for bit clock and frame
- clock providing.
- "pll8k", "pll11k" are optional, they are the clock
- source for root clock, one is for 8kHz series rates
- another one is for 11kHz series rates.
- - dmas : Generic dma devicetree binding as described in
- Documentation/devicetree/bindings/dma/dma.txt.
-
- - dma-names : Two dmas have to be defined, "tx" and "rx".
-
- - pinctrl-names : Must contain a "default" entry.
-
- - pinctrl-NNN : One property must exist for each entry in
- pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
- for details of the property values.
-
- - lsb-first : Configures whether the LSB or the MSB is transmitted
- first for the fifo data. If this property is absent,
- the MSB is transmitted first as default, or the LSB
- is transmitted first.
-
- - fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
- that SAI will work in the synchronous mode (sync Tx
- with Rx) which means both the transmitter and the
- receiver will send and receive data by following
- receiver's bit clocks and frame sync clocks.
-
- - fsl,sai-asynchronous: This is a boolean property. If present, indicating
- that SAI will work in the asynchronous mode, which
- means both transmitter and receiver will send and
- receive data by following their own bit clocks and
- frame sync clocks separately.
-
- - fsl,dataline : configure the dataline. it has 3 value for each configuration
- first one means the type: I2S(1) or PDM(2)
- second one is dataline mask for 'rx'
- third one is dataline mask for 'tx'.
- for example: fsl,dataline = <1 0xff 0xff 2 0xff 0x11>;
- it means I2S type rx mask is 0xff, tx mask is 0xff, PDM type
- rx mask is 0xff, tx mask is 0x11 (dataline 1 and 4 enabled).
-
-Optional properties:
-
- - big-endian : Boolean property, required if all the SAI
- registers are big-endian rather than little-endian.
-
-Optional properties (for mx6ul):
-
- - fsl,sai-mclk-direction-output: This is a boolean property. If present,
- indicates that SAI will output the SAI MCLK clock.
-
-Note:
-- If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
- default synchronous mode (sync Rx with Tx) will be used, which means both
- transmitter and receiver will send and receive data by following clocks
- of transmitter.
-- fsl,sai-asynchronous and fsl,sai-synchronous-rx are exclusive.
-
-Example:
-sai2: sai@40031000 {
- compatible = "fsl,vf610-sai";
- reg = <0x40031000 0x1000>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_sai2_1>;
- clocks = <&clks VF610_CLK_PLATFORM_BUS>,
- <&clks VF610_CLK_SAI2>,
- <&clks 0>, <&clks 0>;
- clock-names = "bus", "mclk1", "mclk2", "mclk3";
- dma-names = "tx", "rx";
- dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
- <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
- big-endian;
- lsb-first;
-};
diff --git a/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml b/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml
index 233caa0ade87..67ccddd44489 100644
--- a/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml
+++ b/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml
@@ -61,6 +61,8 @@ patternProperties:
cpu:
description: Holds subnode which indicates cpu dai.
type: object
+ additionalProperties: false
+
properties:
sound-dai:
maxItems: 1
@@ -68,6 +70,8 @@ patternProperties:
codec:
description: Holds subnode which indicates codec dai.
type: object
+ additionalProperties: false
+
properties:
sound-dai:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
index bb3a435722c7..b6f5d486600e 100644
--- a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
+++ b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
@@ -58,6 +58,7 @@ patternProperties:
cpu:
description: Holds subnode which indicates cpu dai.
type: object
+ additionalProperties: false
properties:
sound-dai:
maxItems: 1
@@ -65,6 +66,7 @@ patternProperties:
codec:
description: Holds subnode which indicates codec dai.
type: object
+ additionalProperties: false
properties:
sound-dai:
minItems: 1
diff --git a/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml b/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
index 513cd28b2027..d427f7f623db 100644
--- a/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
@@ -43,6 +43,16 @@ properties:
required:
- sound-dai
+ mediatek,adsp:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of MT8186 ADSP platform.
+
+ mediatek,dai-link:
+ $ref: /schemas/types.yaml#/definitions/string-array
+ description:
+ A list of the desired dai-links in the sound card. Each entry is a
+ name defined in the machine driver.
+
additionalProperties: false
required:
diff --git a/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml b/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
index 059a7629b2d3..4fc5b045d3cf 100644
--- a/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
@@ -43,6 +43,16 @@ properties:
required:
- sound-dai
+ mediatek,adsp:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of MT8186 ADSP platform.
+
+ mediatek,dai-link:
+ $ref: /schemas/types.yaml#/definitions/string-array
+ description:
+ A list of the desired dai-links in the sound card. Each entry is a
+ name defined in the machine driver.
+
additionalProperties: false
required:
diff --git a/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
index 4fa179909c62..478be7e3fa29 100644
--- a/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
@@ -30,6 +30,8 @@ properties:
headset-codec:
type: object
+ additionalProperties: false
+
properties:
sound-dai:
$ref: /schemas/types.yaml#/definitions/phandle
@@ -38,6 +40,8 @@ properties:
speaker-codecs:
type: object
+ additionalProperties: false
+
properties:
sound-dai:
minItems: 1
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index e6e27d09783e..a3a4289f713e 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -71,6 +71,8 @@ patternProperties:
cpu:
description: Holds subnode which indicates cpu dai.
type: object
+ additionalProperties: false
+
properties:
sound-dai:
maxItems: 1
@@ -78,6 +80,8 @@ patternProperties:
platform:
description: Holds subnode which indicates platform dai.
type: object
+ additionalProperties: false
+
properties:
sound-dai:
maxItems: 1
@@ -85,6 +89,8 @@ patternProperties:
codec:
description: Holds subnode which indicates codec dai.
type: object
+ additionalProperties: false
+
properties:
sound-dai:
minItems: 1
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
index e17c0245f77a..268895c90bd5 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
@@ -129,6 +129,8 @@ properties:
patternProperties:
"^dvc-[0-1]$":
type: object
+ additionalProperties: false
+
properties:
dmas:
maxItems: 1
@@ -145,7 +147,7 @@ properties:
patternProperties:
"^mix-[0-1]$":
type: object
- # no properties
+ additionalProperties: false
additionalProperties: false
rcar_sound,ctu:
@@ -154,7 +156,7 @@ properties:
patternProperties:
"^ctu-[0-7]$":
type: object
- # no properties
+ additionalProperties: false
additionalProperties: false
rcar_sound,src:
@@ -163,6 +165,8 @@ properties:
patternProperties:
"^src-[0-9]$":
type: object
+ additionalProperties: false
+
properties:
interrupts:
maxItems: 1
@@ -186,6 +190,8 @@ properties:
patternProperties:
"^ssiu-[0-9]+$":
type: object
+ additionalProperties: false
+
properties:
dmas:
maxItems: 2
@@ -206,6 +212,8 @@ properties:
patternProperties:
"^ssi-[0-9]$":
type: object
+ additionalProperties: false
+
properties:
interrupts:
maxItems: 1
@@ -243,6 +251,8 @@ properties:
patternProperties:
"^dai([0-9]+)?$":
type: object
+ additionalProperties: false
+
properties:
playback:
$ref: /schemas/types.yaml#/definitions/phandle-array
diff --git a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml
index a01c4ad929b8..447e013f6e17 100644
--- a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml
@@ -23,6 +23,7 @@ properties:
cpu:
type: object
+ additionalProperties: false
properties:
sound-dai:
minItems: 2
@@ -34,6 +35,7 @@ properties:
- sound-dai
codec:
+ additionalProperties: false
type: object
properties:
sound-dai:
diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
index ec50bcb4af5f..31095913e330 100644
--- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
@@ -19,6 +19,7 @@ properties:
cpu:
type: object
+ additionalProperties: false
properties:
sound-dai:
maxItems: 1
@@ -28,6 +29,7 @@ properties:
codec:
type: object
+ additionalProperties: false
properties:
sound-dai:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/sound/samsung,snow.yaml b/Documentation/devicetree/bindings/sound/samsung,snow.yaml
index 51a83d3c7274..3d49aa4c9be2 100644
--- a/Documentation/devicetree/bindings/sound/samsung,snow.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,snow.yaml
@@ -19,6 +19,7 @@ properties:
codec:
type: object
+ additionalProperties: false
properties:
sound-dai:
description: List of phandles to the CODEC and HDMI IP nodes.
@@ -30,6 +31,7 @@ properties:
cpu:
type: object
+ additionalProperties: false
properties:
sound-dai:
description: Phandle to the Samsung I2S controller.
diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.yaml b/Documentation/devicetree/bindings/sound/st,stm32-sai.yaml
index fe2e15504ebc..1a3abc949505 100644
--- a/Documentation/devicetree/bindings/sound/st,stm32-sai.yaml
+++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.yaml
@@ -60,6 +60,7 @@ required:
patternProperties:
"^audio-controller@[0-9a-f]+$":
type: object
+ additionalProperties: false
description:
Two subnodes corresponding to SAI sub-block instances A et B
can be defined. Subnode can be omitted for unsused sub-block.
diff --git a/Documentation/devicetree/bindings/sound/ti,src4xxx.yaml b/Documentation/devicetree/bindings/sound/ti,src4xxx.yaml
new file mode 100644
index 000000000000..9681b72b4918
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,src4xxx.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,src4xxx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments SRC4392 Device Tree Bindings
+
+description: |
+ The SRC4392 is a digital audio codec that can be connected via
+ I2C or SPI. Currently, only I2C bus is supported.
+
+maintainers:
+ - Matt Flax <flatmax@flatmax.com>
+
+allOf:
+ - $ref: name-prefix.yaml#
+
+properties:
+ compatible:
+ const: ti,src4392
+
+ "#sound-dai-cells":
+ const: 0
+
+ reg:
+ maxItems: 1
+
+required:
+ - "#sound-dai-cells"
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@70 {
+ #sound-dai-cells = <0>;
+ compatible = "ti,src4392";
+ reg = <0x70>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml
index 970b1119898b..a453996c13f2 100644
--- a/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml
+++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mtk-nor.yaml
@@ -85,8 +85,9 @@ examples:
compatible = "mediatek,mt8173-nor";
reg = <0 0x1100d000 0 0xe0>;
interrupts = <1>;
- clocks = <&pericfg CLK_PERI_SPI>, <&topckgen CLK_TOP_SPINFI_IFR_SEL>;
- clock-names = "spi", "sf";
+ clocks = <&pericfg CLK_PERI_SPI>, <&topckgen CLK_TOP_SPINFI_IFR_SEL>,
+ <&pericfg CLK_PERI_NFI>;
+ clock-names = "spi", "sf", "axi";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/MAINTAINERS b/MAINTAINERS
index 8a5012ba6ff9..5f91a6b62f2f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1899,6 +1899,14 @@ F: include/dt-bindings/pinctrl/apple.h
F: include/linux/apple-mailbox.h
F: include/linux/soc/apple/*
+ARM/APPLE MACHINE SOUND DRIVERS
+M: Martin Povišer <povik+lin@cutebit.org>
+L: asahi@lists.linux.dev
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/apple,*
+F: drivers/sound/apple/*
+
ARM/ARTPEC MACHINE SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com>
M: Lars Persson <lars.persson@axis.com>
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 8d4000664fa3..d95b07896a3e 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -298,6 +298,38 @@ int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg)
}
/**
+ * sdw_show_ping_status() - Direct report of PING status, to be used by Peripheral drivers
+ * @bus: SDW bus
+ * @sync_delay: Delay before reading status
+ */
+void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay)
+{
+ u32 status;
+
+ if (!bus->ops->read_ping_status)
+ return;
+
+ /*
+ * wait for peripheral to sync if desired. 10-15ms should be more than
+ * enough in most cases.
+ */
+ if (sync_delay)
+ usleep_range(10000, 15000);
+
+ mutex_lock(&bus->msg_lock);
+
+ status = bus->ops->read_ping_status(bus);
+
+ mutex_unlock(&bus->msg_lock);
+
+ if (!status)
+ dev_warn(bus->dev, "%s: no peripherals attached\n", __func__);
+ else
+ dev_dbg(bus->dev, "PING status: %#x\n", status);
+}
+EXPORT_SYMBOL(sdw_show_ping_status);
+
+/**
* sdw_transfer_defer() - Asynchronously transfer message to a SDW Slave device
* @bus: SDW bus
* @msg: SDW message to be xfered
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 4fbb19557f5e..615b0b63a3e1 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -756,6 +756,14 @@ cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
}
EXPORT_SYMBOL(cdns_reset_page_addr);
+u32 cdns_read_ping_status(struct sdw_bus *bus)
+{
+ struct sdw_cdns *cdns = bus_to_cdns(bus);
+
+ return cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
+}
+EXPORT_SYMBOL(cdns_read_ping_status);
+
/*
* IRQ handling
*/
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index 595d72c15d97..ca9e805bab88 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -177,6 +177,8 @@ enum sdw_command_response
cdns_xfer_msg_defer(struct sdw_bus *bus,
struct sdw_msg *msg, struct sdw_defer *defer);
+u32 cdns_read_ping_status(struct sdw_bus *bus);
+
int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params);
int cdns_set_sdw_stream(struct snd_soc_dai *dai,
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 89d1d0d021fc..a5965e8827b9 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -1262,6 +1262,7 @@ static struct sdw_master_ops sdw_intel_ops = {
.set_bus_conf = cdns_bus_conf,
.pre_bank_switch = intel_pre_bank_switch,
.post_bank_switch = intel_post_bank_switch,
+ .read_ping_status = cdns_read_ping_status,
};
static int intel_init(struct sdw_intel *sdw)
diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
index 0d3276c8fc11..9f7c5103bc82 100644
--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
@@ -193,6 +193,24 @@
#define LPASS_CLK_ID_RX_CORE_MCLK 59
#define LPASS_CLK_ID_RX_CORE_NPL_MCLK 60
#define LPASS_CLK_ID_VA_CORE_2X_MCLK 61
+/* Clock ID for MCLK for WSA2 core */
+#define LPASS_CLK_ID_WSA2_CORE_MCLK 62
+/* Clock ID for NPL MCLK for WSA2 core */
+#define LPASS_CLK_ID_WSA2_CORE_2X_MCLK 63
+/* Clock ID for RX Core TX MCLK */
+#define LPASS_CLK_ID_RX_CORE_TX_MCLK 64
+/* Clock ID for RX CORE TX 2X MCLK */
+#define LPASS_CLK_ID_RX_CORE_TX_2X_MCLK 65
+/* Clock ID for WSA core TX MCLK */
+#define LPASS_CLK_ID_WSA_CORE_TX_MCLK 66
+/* Clock ID for WSA core TX 2X MCLK */
+#define LPASS_CLK_ID_WSA_CORE_TX_2X_MCLK 67
+/* Clock ID for WSA2 core TX MCLK */
+#define LPASS_CLK_ID_WSA2_CORE_TX_MCLK 68
+/* Clock ID for WSA2 core TX 2X MCLK */
+#define LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK 69
+/* Clock ID for RX CORE MCLK2 2X MCLK */
+#define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70
#define LPASS_HW_AVTIMER_VOTE 101
#define LPASS_HW_MACRO_VOTE 102
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 39058c841469..822599957b35 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -839,6 +839,8 @@ struct sdw_defer {
* @set_bus_conf: Set the bus configuration
* @pre_bank_switch: Callback for pre bank switch
* @post_bank_switch: Callback for post bank switch
+ * @read_ping_status: Read status from PING frames, reported with two bits per Device.
+ * Bits 31:24 are reserved.
*/
struct sdw_master_ops {
int (*read_prop)(struct sdw_bus *bus);
@@ -855,6 +857,7 @@ struct sdw_master_ops {
struct sdw_bus_params *params);
int (*pre_bank_switch)(struct sdw_bus *bus);
int (*post_bank_switch)(struct sdw_bus *bus);
+ u32 (*read_ping_status)(struct sdw_bus *bus);
};
@@ -919,6 +922,8 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
struct fwnode_handle *fwnode);
void sdw_bus_master_delete(struct sdw_bus *bus);
+void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay);
+
/**
* sdw_port_config: Master or Slave Port configuration
*
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index ab55f40896e0..a0b827f0c2f6 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -39,6 +39,7 @@ struct asoc_simple_dai {
struct asoc_simple_data {
u32 convert_rate;
u32 convert_channels;
+ const char *convert_sample_format;
};
struct asoc_simple_jack {
diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h
index bc7fd46ec2bc..82a7db23db69 100644
--- a/include/sound/soc-acpi-intel-match.h
+++ b/include/sound/soc-acpi-intel-match.h
@@ -14,7 +14,6 @@
* these tables are not constants, some fields can be used for
* pdata or machine ops
*/
-extern struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[];
@@ -30,6 +29,7 @@ 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[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[];
@@ -38,6 +38,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[];
/*
diff --git a/include/sound/soc.h b/include/sound/soc.h
index aad24a1d3276..4351d86eedf6 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -31,31 +31,31 @@
#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = shift_left, \
- .rshift = shift_right, .max = xmax, .platform_max = xmax, \
+ .rshift = shift_right, .max = xmax, \
.invert = xinvert, .autodisable = xautodisable})
#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = shift_left, \
- .rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \
+ .rshift = shift_right, .min = xmin, .max = xmax, \
.sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable})
#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
- {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert})
+ {.reg = xreg, .max = xmax, .invert = xinvert})
#define SOC_DOUBLE_R_VALUE(xlreg, xrreg, xshift, xmax, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
- .max = xmax, .platform_max = xmax, .invert = xinvert})
+ .max = xmax, .invert = xinvert})
#define SOC_DOUBLE_R_S_VALUE(xlreg, xrreg, xshift, xmin, xmax, xsign_bit, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
- .max = xmax, .min = xmin, .platform_max = xmax, .sign_bit = xsign_bit, \
+ .max = xmax, .min = xmin, .sign_bit = xsign_bit, \
.invert = xinvert})
#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
- .min = xmin, .max = xmax, .platform_max = xmax, .invert = xinvert})
+ .min = xmin, .max = xmax, .invert = xinvert})
#define SOC_SINGLE(xname, reg, shift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
@@ -68,7 +68,7 @@
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = xshift, \
.rshift = xshift, .min = xmin, .max = xmax, \
- .platform_max = xmax, .invert = xinvert} }
+ .invert = xinvert} }
#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
@@ -99,7 +99,7 @@
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = xshift, \
.rshift = xshift, .min = xmin, .max = xmax, \
- .platform_max = xmax, .invert = xinvert} }
+ .invert = xinvert} }
#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
@@ -199,7 +199,7 @@
.put = snd_soc_put_volsw, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, \
- .min = xmin, .max = xmax, .platform_max = xmax, \
+ .min = xmin, .max = xmax, \
.sign_bit = 7,} }
#define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
@@ -273,7 +273,7 @@
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = xshift, \
.rshift = xshift, .min = xmin, .max = xmax, \
- .platform_max = xmax, .invert = xinvert} }
+ .invert = xinvert} }
#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
xhandler_get, xhandler_put, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
diff --git a/include/sound/sof.h b/include/sound/sof.h
index 367dccfea7ad..341fef19e612 100644
--- a/include/sound/sof.h
+++ b/include/sound/sof.h
@@ -89,6 +89,7 @@ struct snd_sof_pdata {
/* machine */
struct platform_device *pdev_mach;
const struct snd_soc_acpi_mach *machine;
+ const struct snd_sof_of_mach *of_machine;
void *hw_pdata;
@@ -102,6 +103,7 @@ struct snd_sof_pdata {
struct sof_dev_desc {
/* list of machines using this configuration */
struct snd_soc_acpi_mach *machines;
+ struct snd_sof_of_mach *of_machines;
/* alternate list of machines using this configuration */
struct snd_soc_acpi_mach *alt_machines;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7d4747b6bab2..848fbae26c3b 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -68,6 +68,7 @@ config SND_SOC_ACPI
# All the supported SoCs
source "sound/soc/adi/Kconfig"
source "sound/soc/amd/Kconfig"
+source "sound/soc/apple/Kconfig"
source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
source "sound/soc/bcm/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 453181ef6c94..507eaed1d6a1 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
+obj-$(CONFIG_SND_SOC) += apple/
obj-$(CONFIG_SND_SOC) += adi/
obj-$(CONFIG_SND_SOC) += amd/
obj-$(CONFIG_SND_SOC) += atmel/
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
index 393f729ef561..ac416572db0d 100644
--- a/sound/soc/amd/acp/acp-i2s.c
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -25,6 +25,65 @@
#define DRV_NAME "acp_i2s_playcap"
+static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct acp_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai);
+ int mode;
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai);
+ struct acp_stream *stream;
+ int slot_len;
+
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported bitdepth %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&adata->acp_lock);
+ list_for_each_entry(stream, &adata->stream_list, list) {
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ adata->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ adata->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ }
+ spin_unlock_irq(&adata->acp_lock);
+ return 0;
+}
+
static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
@@ -33,7 +92,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
struct acp_resource *rsrc;
u32 val;
u32 xfer_resolution;
- u32 reg_val;
+ u32 reg_val, fmt_reg, tdm_fmt;
u32 lrclk_div_val, bclk_div_val;
adata = snd_soc_dai_get_drvdata(dai);
@@ -62,12 +121,15 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
break;
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
break;
case I2S_HS_INSTANCE:
reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
@@ -77,12 +139,15 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
break;
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
break;
case I2S_HS_INSTANCE:
reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
@@ -95,6 +160,16 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
val = val | (xfer_resolution << 3);
writel(val, adata->acp_base + reg_val);
+ if (adata->tdm_mode) {
+ val = readl(adata->acp_base + reg_val);
+ writel(val | BIT(1), adata->acp_base + reg_val);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tdm_fmt = adata->tdm_tx_fmt[dai->driver->id - 1];
+ else
+ tdm_fmt = adata->tdm_rx_fmt[dai->driver->id - 1];
+ writel(tdm_fmt, adata->acp_base + fmt_reg);
+ }
+
if (rsrc->soc_mclk) {
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -443,6 +518,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d
stream->id = dai->driver->id + dir;
stream->dai_id = dai->driver->id;
stream->irq_bit = irq_bit;
+ stream->dir = substream->stream;
return 0;
}
@@ -452,6 +528,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
.hw_params = acp_i2s_hwparams,
.prepare = acp_i2s_prepare,
.trigger = acp_i2s_trigger,
+ .set_fmt = acp_i2s_set_fmt,
+ .set_tdm_slot = acp_i2s_set_tdm_slot,
};
EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index 2c8e960cc9a6..ef2ce083521e 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -62,10 +62,9 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
if (!chip)
return -ENOMEM;
- if (pci_enable_device(pci)) {
- dev_err(&pci->dev, "pci_enable_device failed\n");
- return -ENODEV;
- }
+ if (pci_enable_device(pci))
+ return dev_err_probe(&pci->dev, -ENODEV,
+ "pci_enable_device failed\n");
ret = pci_request_regions(pci, "AMD ACP3x audio");
if (ret < 0) {
@@ -105,14 +104,13 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
if (!chip->base) {
ret = -ENOMEM;
- goto release_regions;
+ goto unregister_dmic_dev;
}
res = devm_kzalloc(&pci->dev, sizeof(struct resource) * num_res, GFP_KERNEL);
if (!res) {
- platform_device_unregister(dmic_dev);
ret = -ENOMEM;
- goto release_regions;
+ goto unregister_dmic_dev;
}
for (i = 0; i < num_res; i++, res_acp++) {
@@ -139,13 +137,14 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) {
dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
- platform_device_unregister(dmic_dev);
ret = PTR_ERR(pdev);
- goto release_regions;
+ goto unregister_dmic_dev;
}
return ret;
+unregister_dmic_dev:
+ platform_device_unregister(dmic_dev);
release_regions:
pci_release_regions(pci);
disable_pci:
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
index f561d39b33e2..85a81add4ef9 100644
--- a/sound/soc/amd/acp/acp-platform.c
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -94,7 +94,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *data)
struct acp_resource *rsrc = adata->rsrc;
struct acp_stream *stream;
u16 i2s_flag = 0;
- u32 ext_intr_stat, ext_intr_stat1, i;
+ u32 ext_intr_stat, ext_intr_stat1;
if (!adata)
return IRQ_NONE;
@@ -104,25 +104,24 @@ static irqreturn_t i2s_irq_handler(int irq, void *data)
ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
- for (i = 0; i < ACP_MAX_STREAM; i++) {
- stream = adata->stream[i];
- if (stream && (ext_intr_stat & stream->irq_bit)) {
+ spin_lock(&adata->acp_lock);
+ list_for_each_entry(stream, &adata->stream_list, list) {
+ if (ext_intr_stat & stream->irq_bit) {
writel(stream->irq_bit,
ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
snd_pcm_period_elapsed(stream->substream);
i2s_flag = 1;
- break;
}
if (adata->rsrc->no_of_ctrls == 2) {
- if (stream && (ext_intr_stat1 & stream->irq_bit)) {
+ if (ext_intr_stat1 & stream->irq_bit) {
writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata,
(rsrc->irqp_used - 1)));
snd_pcm_period_elapsed(stream->substream);
i2s_flag = 1;
- break;
}
}
}
+ spin_unlock(&adata->acp_lock);
if (i2s_flag)
return IRQ_HANDLED;
@@ -146,9 +145,8 @@ static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream
writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
}
-static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
+static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size)
{
- struct acp_stream *stream = adata->stream[cpu_id];
struct snd_pcm_substream *substream = stream->substream;
struct acp_resource *rsrc = adata->rsrc;
dma_addr_t addr = substream->dma_buffer.addr;
@@ -174,13 +172,10 @@ static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct snd_pcm_runtime *runtime = substream->runtime;
struct device *dev = component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_stream *stream;
- int stream_id = cpu_dai->driver->id * 2 + substream->stream;
int ret;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
@@ -188,7 +183,10 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs
return -ENOMEM;
stream->substream = substream;
- adata->stream[stream_id] = stream;
+
+ spin_lock_irq(&adata->acp_lock);
+ list_add_tail(&stream->list, &adata->stream_list);
+ spin_unlock_irq(&adata->acp_lock);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = acp_pcm_hardware_playback;
@@ -212,16 +210,13 @@ static int acp_dma_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 = asoc_substream_to_rtd(substream);
struct acp_dev_data *adata = snd_soc_component_get_drvdata(component);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct acp_stream *stream = substream->runtime->private_data;
- int stream_id = cpu_dai->driver->id * 2 + substream->stream;
u64 size = params_buffer_bytes(params);
/* Configure ACP DMA block with params */
config_pte_for_stream(adata, stream);
- config_acp_dma(adata, stream_id, size);
+ config_acp_dma(adata, stream, size);
return 0;
}
@@ -261,16 +256,15 @@ static int acp_dma_new(struct snd_soc_component *component,
static int acp_dma_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
struct device *dev = component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
- struct acp_stream *stream;
- int stream_id = cpu_dai->driver->id * 2 + substream->stream;
+ struct acp_stream *stream = substream->runtime->private_data;
- stream = adata->stream[stream_id];
+ /* Remove entry from list */
+ spin_lock_irq(&adata->acp_lock);
+ list_del(&stream->list);
+ spin_unlock_irq(&adata->acp_lock);
kfree(stream);
- adata->stream[stream_id] = NULL;
return 0;
}
@@ -305,6 +299,10 @@ int acp_platform_register(struct device *dev)
dev_err(dev, "Fail to register acp i2s component\n");
return status;
}
+
+ INIT_LIST_HEAD(&adata->stream_list);
+ spin_lock_init(&adata->acp_lock);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON);
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index af9603724a68..5f2119f42271 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -21,9 +21,9 @@
#define ACP3X_DEV 3
#define ACP6X_DEV 6
-#define I2S_SP_INSTANCE 0x00
-#define I2S_BT_INSTANCE 0x01
-#define DMIC_INSTANCE 0x02
+#define DMIC_INSTANCE 0x00
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
#define I2S_HS_INSTANCE 0x03
#define MEM_WINDOW_START 0x4080000
@@ -84,6 +84,14 @@
#define ACP_MAX_STREAM 8
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+
+#define SLOT_WIDTH_8 0x8
+#define SLOT_WIDTH_16 0x10
+#define SLOT_WIDTH_24 0x18
+#define SLOT_WIDTH_32 0x20
+
struct acp_chip_info {
char *name; /* Platform name */
unsigned int acp_rev; /* ACP Revision id */
@@ -91,10 +99,12 @@ struct acp_chip_info {
};
struct acp_stream {
+ struct list_head list;
struct snd_pcm_substream *substream;
int irq_bit;
int dai_id;
int id;
+ int dir;
u64 bytescount;
u32 reg_offset;
u32 pte_offset;
@@ -119,11 +129,13 @@ struct acp_dev_data {
void __iomem *acp_base;
unsigned int i2s_irq;
+ bool tdm_mode;
/* SOC specific dais */
struct snd_soc_dai_driver *dai_driver;
int num_dai;
- struct acp_stream *stream[ACP_MAX_STREAM];
+ struct list_head stream_list;
+ spinlock_t acp_lock;
struct snd_soc_acpi_mach *machines;
struct platform_device *mach_dev;
@@ -132,6 +144,8 @@ struct acp_dev_data {
u32 lrclk_div;
struct acp_resource *rsrc;
+ u32 tdm_tx_fmt[3];
+ u32 tdm_rx_fmt[3];
};
union acp_i2stdm_mstrclkgen {
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index ecfe7a790790..e0b24e1daef3 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -143,6 +143,34 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
}
},
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
+ }
+ },
{}
};
diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig
new file mode 100644
index 000000000000..0ba955657e98
--- /dev/null
+++ b/sound/soc/apple/Kconfig
@@ -0,0 +1,9 @@
+config SND_SOC_APPLE_MCA
+ tristate "Apple Silicon MCA driver"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select SND_DMAENGINE_PCM
+ select COMMON_CLK
+ default ARCH_APPLE
+ help
+ This option enables an ASoC platform driver for MCA peripherals found
+ on Apple Silicon SoCs.
diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile
new file mode 100644
index 000000000000..7a30bf452817
--- /dev/null
+++ b/sound/soc/apple/Makefile
@@ -0,0 +1,3 @@
+snd-soc-apple-mca-objs := mca.o
+
+obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o
diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
new file mode 100644
index 000000000000..aa67d57c9a9b
--- /dev/null
+++ b/sound/soc/apple/mca.c
@@ -0,0 +1,1167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Apple SoCs MCA driver
+//
+// Copyright (C) The Asahi Linux Contributors
+//
+// The MCA peripheral is made up of a number of identical units called clusters.
+// Each cluster has its separate clock parent, SYNC signal generator, carries
+// four SERDES units and has a dedicated I2S port on the SoC's periphery.
+//
+// The clusters can operate independently, or can be combined together in a
+// configurable manner. We mostly treat them as self-contained independent
+// units and don't configure any cross-cluster connections except for the I2S
+// ports. The I2S ports can be routed to any of the clusters (irrespective
+// of their native cluster). We map this onto ASoC's (DPCM) notion of backend
+// and frontend DAIs. The 'cluster guts' are frontends which are dynamically
+// routed to backend I2S ports.
+//
+// DAI references in devicetree are resolved to backends. The routing between
+// frontends and backends is determined by the machine driver in the DAPM paths
+// it supplies.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define USE_RXB_FOR_CAPTURE
+
+/* Relative to cluster base */
+#define REG_STATUS 0x0
+#define STATUS_MCLK_EN BIT(0)
+#define REG_MCLK_CONF 0x4
+#define MCLK_CONF_DIV GENMASK(11, 8)
+
+#define REG_SYNCGEN_STATUS 0x100
+#define SYNCGEN_STATUS_EN BIT(0)
+#define REG_SYNCGEN_MCLK_SEL 0x104
+#define SYNCGEN_MCLK_SEL GENMASK(3, 0)
+#define REG_SYNCGEN_HI_PERIOD 0x108
+#define REG_SYNCGEN_LO_PERIOD 0x10c
+
+#define REG_PORT_ENABLES 0x600
+#define PORT_ENABLES_CLOCKS GENMASK(2, 1)
+#define PORT_ENABLES_TX_DATA BIT(3)
+#define REG_PORT_CLOCK_SEL 0x604
+#define PORT_CLOCK_SEL GENMASK(11, 8)
+#define REG_PORT_DATA_SEL 0x608
+#define PORT_DATA_SEL_TXA(cl) (1 << ((cl)*2))
+#define PORT_DATA_SEL_TXB(cl) (2 << ((cl)*2))
+
+#define REG_INTSTATE 0x700
+#define REG_INTMASK 0x704
+
+/* Bases of serdes units (relative to cluster) */
+#define CLUSTER_RXA_OFF 0x200
+#define CLUSTER_TXA_OFF 0x300
+#define CLUSTER_RXB_OFF 0x400
+#define CLUSTER_TXB_OFF 0x500
+
+#define CLUSTER_TX_OFF CLUSTER_TXA_OFF
+
+#ifndef USE_RXB_FOR_CAPTURE
+#define CLUSTER_RX_OFF CLUSTER_RXA_OFF
+#else
+#define CLUSTER_RX_OFF CLUSTER_RXB_OFF
+#endif
+
+/* Relative to serdes unit base */
+#define REG_SERDES_STATUS 0x00
+#define SERDES_STATUS_EN BIT(0)
+#define SERDES_STATUS_RST BIT(1)
+#define REG_TX_SERDES_CONF 0x04
+#define REG_RX_SERDES_CONF 0x08
+#define SERDES_CONF_NCHANS GENMASK(3, 0)
+#define SERDES_CONF_WIDTH_MASK GENMASK(8, 4)
+#define SERDES_CONF_WIDTH_16BIT 0x40
+#define SERDES_CONF_WIDTH_20BIT 0x80
+#define SERDES_CONF_WIDTH_24BIT 0xc0
+#define SERDES_CONF_WIDTH_32BIT 0x100
+#define SERDES_CONF_BCLK_POL 0x400
+#define SERDES_CONF_LSB_FIRST 0x800
+#define SERDES_CONF_UNK1 BIT(12)
+#define SERDES_CONF_UNK2 BIT(13)
+#define SERDES_CONF_UNK3 BIT(14)
+#define SERDES_CONF_NO_DATA_FEEDBACK BIT(15)
+#define SERDES_CONF_SYNC_SEL GENMASK(18, 16)
+#define SERDES_CONF_SOME_RST BIT(19)
+#define REG_TX_SERDES_BITSTART 0x08
+#define REG_RX_SERDES_BITSTART 0x0c
+#define REG_TX_SERDES_SLOTMASK 0x0c
+#define REG_RX_SERDES_SLOTMASK 0x10
+#define REG_RX_SERDES_PORT 0x04
+
+/* Relative to switch base */
+#define REG_DMA_ADAPTER_A(cl) (0x8000 * (cl))
+#define REG_DMA_ADAPTER_B(cl) (0x8000 * (cl) + 0x4000)
+#define DMA_ADAPTER_TX_LSB_PAD GENMASK(4, 0)
+#define DMA_ADAPTER_TX_NCHANS GENMASK(6, 5)
+#define DMA_ADAPTER_RX_MSB_PAD GENMASK(12, 8)
+#define DMA_ADAPTER_RX_NCHANS GENMASK(14, 13)
+#define DMA_ADAPTER_NCHANS GENMASK(22, 20)
+
+#define SWITCH_STRIDE 0x8000
+#define CLUSTER_STRIDE 0x4000
+
+#define MAX_NCLUSTERS 6
+
+#define APPLE_MCA_FMTBITS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct mca_cluster {
+ int no;
+ __iomem void *base;
+ struct mca_data *host;
+ struct device *pd_dev;
+ struct clk *clk_parent;
+ struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1];
+
+ bool port_started[SNDRV_PCM_STREAM_LAST + 1];
+ int port_driver; /* The cluster driving this cluster's port */
+
+ bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1];
+ struct device_link *pd_link;
+
+ unsigned int bclk_ratio;
+
+ /* Masks etc. picked up via the set_tdm_slot method */
+ int tdm_slots;
+ int tdm_slot_width;
+ unsigned int tdm_tx_mask;
+ unsigned int tdm_rx_mask;
+};
+
+struct mca_data {
+ struct device *dev;
+
+ __iomem void *switch_base;
+
+ struct device *pd_dev;
+ struct reset_control *rstc;
+ struct device_link *pd_link;
+
+ /* Mutex for accessing port_driver of foreign clusters */
+ struct mutex port_mutex;
+
+ int nclusters;
+ struct mca_cluster clusters[];
+};
+
+static void mca_modify(struct mca_cluster *cl, int regoffset, u32 mask, u32 val)
+{
+ __iomem void *ptr = cl->base + regoffset;
+ u32 newval;
+
+ newval = (val & mask) | (readl_relaxed(ptr) & ~mask);
+ writel_relaxed(newval, ptr);
+}
+
+/*
+ * Get the cluster of FE or BE DAI
+ */
+static struct mca_cluster *mca_dai_to_cluster(struct snd_soc_dai *dai)
+{
+ struct mca_data *mca = snd_soc_dai_get_drvdata(dai);
+ /*
+ * FE DAIs are 0 ... nclusters - 1
+ * BE DAIs are nclusters ... 2*nclusters - 1
+ */
+ int cluster_no = dai->id % mca->nclusters;
+
+ return &mca->clusters[cluster_no];
+}
+
+/* called before PCM trigger */
+static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
+ int serdes_conf =
+ serdes_unit + (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN | SERDES_STATUS_RST,
+ SERDES_STATUS_RST);
+ mca_modify(cl, serdes_conf, SERDES_CONF_SOME_RST,
+ SERDES_CONF_SOME_RST);
+ readl_relaxed(cl->base + serdes_conf);
+ mca_modify(cl, serdes_conf, SERDES_STATUS_RST, 0);
+ WARN_ON(readl_relaxed(cl->base + REG_SERDES_STATUS) &
+ SERDES_STATUS_RST);
+ break;
+ default:
+ break;
+ }
+}
+
+static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN | SERDES_STATUS_RST,
+ SERDES_STATUS_EN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
+ SERDES_STATUS_EN, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mca_fe_enable_clocks(struct mca_cluster *cl)
+{
+ struct mca_data *mca = cl->host;
+ int ret;
+
+ ret = clk_prepare_enable(cl->clk_parent);
+ if (ret) {
+ dev_err(mca->dev,
+ "cluster %d: unable to enable clock parent: %d\n",
+ cl->no, ret);
+ return ret;
+ }
+
+ /*
+ * We can't power up the device earlier than this because
+ * the power state driver would error out on seeing the device
+ * as clock-gated.
+ */
+ cl->pd_link = device_link_add(mca->dev, cl->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!cl->pd_link) {
+ dev_err(mca->dev,
+ "cluster %d: unable to prop-up power domain\n", cl->no);
+ clk_disable_unprepare(cl->clk_parent);
+ return -EINVAL;
+ }
+
+ writel_relaxed(cl->no + 1, cl->base + REG_SYNCGEN_MCLK_SEL);
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN,
+ SYNCGEN_STATUS_EN);
+ mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, STATUS_MCLK_EN);
+
+ return 0;
+}
+
+static void mca_fe_disable_clocks(struct mca_cluster *cl)
+{
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0);
+ mca_modify(cl, REG_STATUS, STATUS_MCLK_EN, 0);
+
+ device_link_del(cl->pd_link);
+ clk_disable_unprepare(cl->clk_parent);
+}
+
+static bool mca_fe_clocks_in_use(struct mca_cluster *cl)
+{
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *be_cl;
+ int stream, i;
+
+ mutex_lock(&mca->port_mutex);
+ for (i = 0; i < mca->nclusters; i++) {
+ be_cl = &mca->clusters[i];
+
+ if (be_cl->port_driver != cl->no)
+ continue;
+
+ for_each_pcm_streams(stream) {
+ if (be_cl->clocks_in_use[stream]) {
+ mutex_unlock(&mca->port_mutex);
+ return true;
+ }
+ }
+ }
+ mutex_unlock(&mca->port_mutex);
+ return false;
+}
+
+static int mca_be_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl;
+ int ret;
+
+ if (cl->port_driver < 0)
+ return -EINVAL;
+
+ fe_cl = &mca->clusters[cl->port_driver];
+
+ /*
+ * Typically the CODECs we are paired with will require clocks
+ * to be present at time of unmute with the 'mute_stream' op
+ * or at time of DAPM widget power-up. We need to enable clocks
+ * here at the latest (frontend prepare would be too late).
+ */
+ if (!mca_fe_clocks_in_use(fe_cl)) {
+ ret = mca_fe_enable_clocks(fe_cl);
+ if (ret < 0)
+ return ret;
+ }
+
+ cl->clocks_in_use[substream->stream] = true;
+
+ return 0;
+}
+
+static int mca_be_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl;
+
+ if (cl->port_driver < 0)
+ return -EINVAL;
+
+ /*
+ * We are operating on a foreign cluster here, but since we
+ * belong to the same PCM, accesses should have been
+ * synchronized at ASoC level.
+ */
+ fe_cl = &mca->clusters[cl->port_driver];
+ if (!mca_fe_clocks_in_use(fe_cl))
+ return 0; /* Nothing to do */
+
+ cl->clocks_in_use[substream->stream] = false;
+
+ if (!mca_fe_clocks_in_use(fe_cl))
+ mca_fe_disable_clocks(fe_cl);
+
+ return 0;
+}
+
+static unsigned int mca_crop_mask(unsigned int mask, int nchans)
+{
+ while (hweight32(mask) > nchans)
+ mask &= ~(1 << __fls(mask));
+
+ return mask;
+}
+
+static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit,
+ unsigned int mask, int slots, int nchans,
+ int slot_width, bool is_tx, int port)
+{
+ __iomem void *serdes_base = cl->base + serdes_unit;
+ u32 serdes_conf, serdes_conf_mask;
+
+ serdes_conf_mask = SERDES_CONF_WIDTH_MASK | SERDES_CONF_NCHANS;
+ serdes_conf = FIELD_PREP(SERDES_CONF_NCHANS, max(slots, 1) - 1);
+ switch (slot_width) {
+ case 16:
+ serdes_conf |= SERDES_CONF_WIDTH_16BIT;
+ break;
+ case 20:
+ serdes_conf |= SERDES_CONF_WIDTH_20BIT;
+ break;
+ case 24:
+ serdes_conf |= SERDES_CONF_WIDTH_24BIT;
+ break;
+ case 32:
+ serdes_conf |= SERDES_CONF_WIDTH_32BIT;
+ break;
+ default:
+ goto err;
+ }
+
+ serdes_conf_mask |= SERDES_CONF_SYNC_SEL;
+ serdes_conf |= FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1);
+
+ if (is_tx) {
+ serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3;
+ serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3;
+ } else {
+ serdes_conf_mask |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_UNK3 |
+ SERDES_CONF_NO_DATA_FEEDBACK;
+ serdes_conf |= SERDES_CONF_UNK1 | SERDES_CONF_UNK2 |
+ SERDES_CONF_NO_DATA_FEEDBACK;
+ }
+
+ mca_modify(cl,
+ serdes_unit +
+ (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF),
+ serdes_conf_mask, serdes_conf);
+
+ if (is_tx) {
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_TX_SERDES_SLOTMASK);
+ writel_relaxed(~((u32)mca_crop_mask(mask, nchans)),
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0x4);
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0x8);
+ writel_relaxed(~((u32)mask),
+ serdes_base + REG_TX_SERDES_SLOTMASK + 0xc);
+ } else {
+ writel_relaxed(0xffffffff,
+ serdes_base + REG_RX_SERDES_SLOTMASK);
+ writel_relaxed(~((u32)mca_crop_mask(mask, nchans)),
+ serdes_base + REG_RX_SERDES_SLOTMASK + 0x4);
+ writel_relaxed(1 << port,
+ serdes_base + REG_RX_SERDES_PORT);
+ }
+
+ return 0;
+
+err:
+ dev_err(cl->host->dev,
+ "unsupported SERDES configuration requested (mask=0x%x slots=%d slot_width=%d)\n",
+ mask, slots, slot_width);
+ return -EINVAL;
+}
+
+static int mca_fe_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ cl->tdm_slots = slots;
+ cl->tdm_slot_width = slot_width;
+ cl->tdm_tx_mask = tx_mask;
+ cl->tdm_rx_mask = rx_mask;
+
+ return 0;
+}
+
+static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ bool fpol_inv = false;
+ u32 serdes_conf = 0;
+ u32 bitstart;
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) !=
+ SND_SOC_DAIFMT_BP_FP)
+ goto err;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fpol_inv = 0;
+ bitstart = 1;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fpol_inv = 1;
+ bitstart = 0;
+ break;
+ default:
+ goto err;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ fpol_inv ^= 1;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ serdes_conf |= SERDES_CONF_BCLK_POL;
+ break;
+ }
+
+ if (!fpol_inv)
+ goto err;
+
+ mca_modify(cl, CLUSTER_TX_OFF + REG_TX_SERDES_CONF,
+ SERDES_CONF_BCLK_POL, serdes_conf);
+ mca_modify(cl, CLUSTER_RX_OFF + REG_RX_SERDES_CONF,
+ SERDES_CONF_BCLK_POL, serdes_conf);
+ writel_relaxed(bitstart,
+ cl->base + CLUSTER_TX_OFF + REG_TX_SERDES_BITSTART);
+ writel_relaxed(bitstart,
+ cl->base + CLUSTER_RX_OFF + REG_RX_SERDES_BITSTART);
+
+ return 0;
+
+err:
+ dev_err(mca->dev, "unsupported DAI format (0x%x) requested\n", fmt);
+ return -EINVAL;
+}
+
+static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ cl->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static int mca_fe_get_port(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = NULL;
+ for_each_dpcm_be(fe, substream->stream, dpcm) {
+ be = dpcm->be;
+ break;
+ }
+
+ if (!be)
+ return -EINVAL;
+
+ return mca_dai_to_cluster(asoc_rtd_to_cpu(be, 0))->no;
+}
+
+static int mca_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct device *dev = mca->dev;
+ unsigned int samp_rate = params_rate(params);
+ bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ bool refine_tdm = false;
+ unsigned long bclk_ratio;
+ unsigned int tdm_slots, tdm_slot_width, tdm_mask;
+ u32 regval, pad;
+ int ret, port, nchans_ceiled;
+
+ if (!cl->tdm_slot_width) {
+ /*
+ * We were not given TDM settings from above, set initial
+ * guesses which will later be refined.
+ */
+ tdm_slot_width = params_width(params);
+ tdm_slots = params_channels(params);
+ refine_tdm = true;
+ } else {
+ tdm_slot_width = cl->tdm_slot_width;
+ tdm_slots = cl->tdm_slots;
+ tdm_mask = is_tx ? cl->tdm_tx_mask : cl->tdm_rx_mask;
+ }
+
+ if (cl->bclk_ratio)
+ bclk_ratio = cl->bclk_ratio;
+ else
+ bclk_ratio = tdm_slot_width * tdm_slots;
+
+ if (refine_tdm) {
+ int nchannels = params_channels(params);
+
+ if (nchannels > 2) {
+ dev_err(dev, "missing TDM for stream with two or more channels\n");
+ return -EINVAL;
+ }
+
+ if ((bclk_ratio % nchannels) != 0) {
+ dev_err(dev, "BCLK ratio (%ld) not divisible by no. of channels (%d)\n",
+ bclk_ratio, nchannels);
+ return -EINVAL;
+ }
+
+ tdm_slot_width = bclk_ratio / nchannels;
+
+ if (tdm_slot_width > 32 && nchannels == 1)
+ tdm_slot_width = 32;
+
+ if (tdm_slot_width < params_width(params)) {
+ dev_err(dev, "TDM slots too narrow (tdm=%d params=%d)\n",
+ tdm_slot_width, params_width(params));
+ return -EINVAL;
+ }
+
+ tdm_mask = (1 << tdm_slots) - 1;
+ }
+
+ port = mca_fe_get_port(substream);
+ if (port < 0)
+ return port;
+
+ ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF,
+ tdm_mask, tdm_slots, params_channels(params),
+ tdm_slot_width, is_tx, port);
+ if (ret)
+ return ret;
+
+ pad = 32 - params_width(params);
+
+ /*
+ * TODO: Here the register semantics aren't clear.
+ */
+ nchans_ceiled = min_t(int, params_channels(params), 4);
+ regval = FIELD_PREP(DMA_ADAPTER_NCHANS, nchans_ceiled) |
+ FIELD_PREP(DMA_ADAPTER_TX_NCHANS, 0x2) |
+ FIELD_PREP(DMA_ADAPTER_RX_NCHANS, 0x2) |
+ FIELD_PREP(DMA_ADAPTER_TX_LSB_PAD, pad) |
+ FIELD_PREP(DMA_ADAPTER_RX_MSB_PAD, pad);
+
+#ifndef USE_RXB_FOR_CAPTURE
+ writel_relaxed(regval, mca->switch_base + REG_DMA_ADAPTER_A(cl->no));
+#else
+ if (is_tx)
+ writel_relaxed(regval,
+ mca->switch_base + REG_DMA_ADAPTER_A(cl->no));
+ else
+ writel_relaxed(regval,
+ mca->switch_base + REG_DMA_ADAPTER_B(cl->no));
+#endif
+
+ if (!mca_fe_clocks_in_use(cl)) {
+ /*
+ * Set up FSYNC duty cycle as even as possible.
+ */
+ writel_relaxed((bclk_ratio / 2) - 1,
+ cl->base + REG_SYNCGEN_HI_PERIOD);
+ writel_relaxed(((bclk_ratio + 1) / 2) - 1,
+ cl->base + REG_SYNCGEN_LO_PERIOD);
+ writel_relaxed(FIELD_PREP(MCLK_CONF_DIV, 0x1),
+ cl->base + REG_MCLK_CONF);
+
+ ret = clk_set_rate(cl->clk_parent, bclk_ratio * samp_rate);
+ if (ret) {
+ dev_err(mca->dev, "cluster %d: unable to set clock parent: %d\n",
+ cl->no, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mca_fe_ops = {
+ .set_fmt = mca_fe_set_fmt,
+ .set_bclk_ratio = mca_set_bclk_ratio,
+ .set_tdm_slot = mca_fe_set_tdm_slot,
+ .hw_params = mca_fe_hw_params,
+ .trigger = mca_fe_trigger,
+};
+
+static bool mca_be_started(struct mca_cluster *cl)
+{
+ int stream;
+
+ for_each_pcm_streams(stream)
+ if (cl->port_started[stream])
+ return true;
+ return false;
+}
+
+static int mca_be_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *be = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *fe;
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_cluster *fe_cl;
+ struct mca_data *mca = cl->host;
+ struct snd_soc_dpcm *dpcm;
+
+ fe = NULL;
+
+ for_each_dpcm_fe(be, substream->stream, dpcm) {
+ if (fe && dpcm->fe != fe) {
+ dev_err(mca->dev, "many FE per one BE unsupported\n");
+ return -EINVAL;
+ }
+
+ fe = dpcm->fe;
+ }
+
+ if (!fe)
+ return -EINVAL;
+
+ fe_cl = mca_dai_to_cluster(asoc_rtd_to_cpu(fe, 0));
+
+ if (mca_be_started(cl)) {
+ /*
+ * Port is already started in the other direction.
+ * Make sure there isn't a conflict with another cluster
+ * driving the port.
+ */
+ if (cl->port_driver != fe_cl->no)
+ return -EINVAL;
+
+ cl->port_started[substream->stream] = true;
+ return 0;
+ }
+
+ writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA,
+ cl->base + REG_PORT_ENABLES);
+ writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1),
+ cl->base + REG_PORT_CLOCK_SEL);
+ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no),
+ cl->base + REG_PORT_DATA_SEL);
+ mutex_lock(&mca->port_mutex);
+ cl->port_driver = fe_cl->no;
+ mutex_unlock(&mca->port_mutex);
+ cl->port_started[substream->stream] = true;
+
+ return 0;
+}
+
+static void mca_be_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+
+ cl->port_started[substream->stream] = false;
+
+ if (!mca_be_started(cl)) {
+ /*
+ * Were we the last direction to shutdown?
+ * Turn off the lights.
+ */
+ writel_relaxed(0, cl->base + REG_PORT_ENABLES);
+ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL);
+ mutex_lock(&mca->port_mutex);
+ cl->port_driver = -1;
+ mutex_unlock(&mca->port_mutex);
+ }
+}
+
+static const struct snd_soc_dai_ops mca_be_ops = {
+ .prepare = mca_be_prepare,
+ .hw_free = mca_be_hw_free,
+ .startup = mca_be_startup,
+ .shutdown = mca_be_shutdown,
+};
+
+static int mca_set_runtime_hwparams(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct dma_chan *chan)
+{
+ struct device *dma_dev = chan->device->dev;
+ struct snd_dmaengine_dai_dma_data dma_data = {};
+ int ret;
+
+ struct snd_pcm_hardware hw;
+
+ memset(&hw, 0, sizeof(hw));
+
+ hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED;
+ hw.periods_min = 2;
+ hw.periods_max = UINT_MAX;
+ hw.period_bytes_min = 256;
+ hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
+ hw.buffer_bytes_max = SIZE_MAX;
+ hw.fifo_size = 16;
+
+ 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 mca_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0));
+ struct dma_chan *chan = cl->dma_chans[substream->stream];
+ int ret;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ ret = mca_set_runtime_hwparams(component, substream, chan);
+ if (ret)
+ return ret;
+
+ return snd_dmaengine_pcm_open(substream, chan);
+}
+
+static int mca_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct dma_slave_config slave_config;
+ int ret;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ memset(&slave_config, 0, sizeof(slave_config));
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ &slave_config);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ slave_config.dst_port_window_size =
+ min_t(u32, params_channels(params), 4);
+ else
+ slave_config.src_port_window_size =
+ min_t(u32, params_channels(params), 4);
+
+ return dmaengine_slave_config(chan, &slave_config);
+}
+
+static int mca_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ return snd_dmaengine_pcm_close(substream);
+}
+
+static int mca_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ /*
+ * Before we do the PCM trigger proper, insert an opportunity
+ * to reset the frontend's SERDES.
+ */
+ mca_fe_early_trigger(substream, cmd, asoc_rtd_to_cpu(rtd, 0));
+
+ return snd_dmaengine_pcm_trigger(substream, cmd);
+}
+
+static snd_pcm_uframes_t mca_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+
+ if (rtd->dai_link->no_pcm)
+ return -ENOTSUPP;
+
+ return snd_dmaengine_pcm_pointer(substream);
+}
+
+static int mca_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(asoc_rtd_to_cpu(rtd, 0));
+ unsigned int i;
+
+ if (rtd->dai_link->no_pcm)
+ return 0;
+
+ for_each_pcm_streams(i) {
+ struct snd_pcm_substream *substream =
+ rtd->pcm->streams[i].substream;
+ struct dma_chan *chan = cl->dma_chans[i];
+
+ if (!substream)
+ continue;
+
+ if (!chan) {
+ dev_err(component->dev, "missing DMA channel for stream %d on SERDES %d\n",
+ i, cl->no);
+ return -EINVAL;
+ }
+
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV_IRAM,
+ chan->device->dev, 512 * 1024 * 6,
+ SIZE_MAX);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mca_component = {
+ .name = "apple-mca",
+ .open = mca_pcm_open,
+ .close = mca_close,
+ .hw_params = mca_hw_params,
+ .trigger = mca_trigger,
+ .pointer = mca_pointer,
+ .pcm_construct = mca_pcm_new,
+};
+
+static void apple_mca_release(struct mca_data *mca)
+{
+ int i, stream;
+
+ for (i = 0; i < mca->nclusters; i++) {
+ struct mca_cluster *cl = &mca->clusters[i];
+
+ for_each_pcm_streams(stream) {
+ if (IS_ERR_OR_NULL(cl->dma_chans[stream]))
+ continue;
+
+ dma_release_channel(cl->dma_chans[stream]);
+ }
+
+ if (!IS_ERR_OR_NULL(cl->clk_parent))
+ clk_put(cl->clk_parent);
+
+ if (!IS_ERR_OR_NULL(cl->pd_dev))
+ dev_pm_domain_detach(cl->pd_dev, true);
+ }
+
+ if (mca->pd_link)
+ device_link_del(mca->pd_link);
+
+ if (!IS_ERR_OR_NULL(mca->pd_dev))
+ dev_pm_domain_detach(mca->pd_dev, true);
+
+ reset_control_assert(mca->rstc);
+}
+
+static int apple_mca_probe(struct platform_device *pdev)
+{
+ struct mca_data *mca;
+ struct mca_cluster *clusters;
+ struct snd_soc_dai_driver *dai_drivers;
+ struct resource *res;
+ void __iomem *base;
+ int nclusters;
+ int ret, i;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (resource_size(res) < CLUSTER_STRIDE)
+ return -EINVAL;
+ nclusters = (resource_size(res) - CLUSTER_STRIDE) / CLUSTER_STRIDE + 1;
+
+ mca = devm_kzalloc(&pdev->dev, struct_size(mca, clusters, nclusters),
+ GFP_KERNEL);
+ if (!mca)
+ return -ENOMEM;
+ mca->dev = &pdev->dev;
+ mca->nclusters = nclusters;
+ mutex_init(&mca->port_mutex);
+ platform_set_drvdata(pdev, mca);
+ clusters = mca->clusters;
+
+ mca->switch_base =
+ devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(mca->switch_base))
+ return PTR_ERR(mca->switch_base);
+
+ mca->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(mca->rstc))
+ return PTR_ERR(mca->rstc);
+
+ dai_drivers = devm_kzalloc(
+ &pdev->dev, sizeof(*dai_drivers) * 2 * nclusters, GFP_KERNEL);
+ if (!dai_drivers)
+ return -ENOMEM;
+
+ mca->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, 0);
+ if (IS_ERR(mca->pd_dev))
+ return -EINVAL;
+
+ mca->pd_link = device_link_add(&pdev->dev, mca->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!mca->pd_link) {
+ ret = -EINVAL;
+ /* Prevent an unbalanced reset assert */
+ mca->rstc = NULL;
+ goto err_release;
+ }
+
+ reset_control_deassert(mca->rstc);
+
+ for (i = 0; i < nclusters; i++) {
+ struct mca_cluster *cl = &clusters[i];
+ struct snd_soc_dai_driver *fe =
+ &dai_drivers[mca->nclusters + i];
+ struct snd_soc_dai_driver *be = &dai_drivers[i];
+ int stream;
+
+ cl->host = mca;
+ cl->no = i;
+ cl->base = base + CLUSTER_STRIDE * i;
+ cl->port_driver = -1;
+ cl->clk_parent = of_clk_get(pdev->dev.of_node, i);
+ if (IS_ERR(cl->clk_parent)) {
+ dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n",
+ i, PTR_ERR(cl->clk_parent));
+ ret = PTR_ERR(cl->clk_parent);
+ goto err_release;
+ }
+ cl->pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i + 1);
+ if (IS_ERR(cl->pd_dev)) {
+ dev_err(&pdev->dev,
+ "unable to obtain cluster %d PD: %ld\n", i,
+ PTR_ERR(cl->pd_dev));
+ ret = PTR_ERR(cl->pd_dev);
+ goto err_release;
+ }
+
+ for_each_pcm_streams(stream) {
+ struct dma_chan *chan;
+ bool is_tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+#ifndef USE_RXB_FOR_CAPTURE
+ char *name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ is_tx ? "tx%da" : "rx%da",
+ i);
+#else
+ char *name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ is_tx ? "tx%da" : "rx%db",
+ i);
+#endif
+
+ chan = of_dma_request_slave_channel(pdev->dev.of_node,
+ name);
+ if (IS_ERR(chan)) {
+ if (PTR_ERR(chan) != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "no %s DMA channel: %ld\n",
+ name, PTR_ERR(chan));
+
+ ret = PTR_ERR(chan);
+ goto err_release;
+ }
+
+ cl->dma_chans[stream] = chan;
+ }
+
+ fe->id = i;
+ fe->name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-pcm-%d", i);
+ if (!fe->name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ fe->ops = &mca_fe_ops;
+ fe->playback.channels_min = 1;
+ fe->playback.channels_max = 32;
+ fe->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ fe->playback.formats = APPLE_MCA_FMTBITS;
+ fe->capture.channels_min = 1;
+ fe->capture.channels_max = 32;
+ fe->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ fe->capture.formats = APPLE_MCA_FMTBITS;
+ fe->symmetric_rate = 1;
+
+ fe->playback.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d TX", i);
+ fe->capture.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "PCM%d RX", i);
+
+ if (!fe->playback.stream_name || !fe->capture.stream_name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ be->id = i + nclusters;
+ be->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "mca-i2s-%d", i);
+ if (!be->name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ be->ops = &mca_be_ops;
+ be->playback.channels_min = 1;
+ be->playback.channels_max = 32;
+ be->playback.rates = SNDRV_PCM_RATE_8000_192000;
+ be->playback.formats = APPLE_MCA_FMTBITS;
+ be->capture.channels_min = 1;
+ be->capture.channels_max = 32;
+ be->capture.rates = SNDRV_PCM_RATE_8000_192000;
+ be->capture.formats = APPLE_MCA_FMTBITS;
+
+ be->playback.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d TX", i);
+ be->capture.stream_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "I2S%d RX", i);
+ if (!be->playback.stream_name || !be->capture.stream_name) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &mca_component,
+ dai_drivers, nclusters * 2);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register ASoC component: %d\n",
+ ret);
+ goto err_release;
+ }
+
+ return 0;
+
+err_release:
+ apple_mca_release(mca);
+ return ret;
+}
+
+static int apple_mca_remove(struct platform_device *pdev)
+{
+ struct mca_data *mca = platform_get_drvdata(pdev);
+
+ apple_mca_release(mca);
+ return 0;
+}
+
+static const struct of_device_id apple_mca_of_match[] = {
+ { .compatible = "apple,mca", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, apple_mca_of_match);
+
+static struct platform_driver apple_mca_driver = {
+ .driver = {
+ .name = "apple-mca",
+ .of_match_table = apple_mca_of_match,
+ },
+ .probe = apple_mca_probe,
+ .remove = apple_mca_remove,
+};
+module_platform_driver(apple_mca_driver);
+
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_DESCRIPTION("ASoC Apple MCA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index e868b7e028d6..3763454436c1 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -891,7 +891,6 @@ static int asoc_ssc_init(struct device *dev)
int atmel_ssc_set_audio(int ssc_id)
{
struct ssc_device *ssc;
- int ret;
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
@@ -903,9 +902,7 @@ int atmel_ssc_set_audio(int ssc_id)
ssc_info[ssc_id].ssc = ssc;
}
- ret = asoc_ssc_init(&ssc->pdev->dev);
-
- return ret;
+ return asoc_ssc_init(&ssc->pdev->dev);
}
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c
index 4850a177803d..ab2d7a791f39 100644
--- a/sound/soc/atmel/mchp-spdiftx.c
+++ b/sound/soc/atmel/mchp-spdiftx.c
@@ -196,7 +196,7 @@ struct mchp_spdiftx_dev {
struct clk *pclk;
struct clk *gclk;
unsigned int fmt;
- int gclk_enabled:1;
+ unsigned int gclk_enabled:1;
};
static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev)
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 4d25fb61c652..1430642c8433 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -172,7 +172,7 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
ret = snd_soc_register_card(card);
if (ret) {
dev_err_probe(&pdev->dev, ret,
- "snd_soc_register_card() failed: %d\n", ret);
+ "snd_soc_register_card() failed\n");
goto err;
}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d16b4efb88a7..968d0701f2e8 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -98,6 +98,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_DA9055
imply SND_SOC_DMIC
imply SND_SOC_ES8316
+ imply SND_SOC_ES8326
imply SND_SOC_ES8328_SPI
imply SND_SOC_ES8328_I2C
imply SND_SOC_ES7134
@@ -205,6 +206,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_SIMPLE_AMPLIFIER
imply SND_SOC_SIMPLE_MUX
imply SND_SOC_SPDIF
+ imply SND_SOC_SRC4XXX_I2C
imply SND_SOC_SSM2305
imply SND_SOC_SSM2518
imply SND_SOC_SSM2602_SPI
@@ -608,7 +610,7 @@ config SND_SOC_BT_SCO
config SND_SOC_CPCAP
tristate "Motorola CPCAP codec"
- depends on MFD_CPCAP
+ depends on MFD_CPCAP || COMPILE_TEST
config SND_SOC_CQ0093VC
tristate
@@ -913,6 +915,10 @@ config SND_SOC_ES8316
tristate "Everest Semi ES8316 CODEC"
depends on I2C
+config SND_SOC_ES8326
+ tristate "Everest Semi ES8326 CODEC"
+ depends on I2C
+
config SND_SOC_ES8328
tristate
@@ -966,7 +972,7 @@ config SND_SOC_LM49453
config SND_SOC_LOCHNAGAR_SC
tristate "Lochnagar Sound Card"
- depends on MFD_LOCHNAGAR
+ depends on MFD_LOCHNAGAR || COMPILE_TEST
help
This driver support the sound card functionality of the Cirrus
Logic Lochnagar audio development board.
@@ -1191,7 +1197,7 @@ config SND_SOC_RK3328
config SND_SOC_RK817
tristate "Rockchip RK817 audio CODEC"
- depends on MFD_RK808
+ depends on MFD_RK808 || COMPILE_TEST && I2C
select REGMAP_I2C
config SND_SOC_RL6231
@@ -1471,6 +1477,18 @@ config SND_SOC_SIMPLE_MUX
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
+config SND_SOC_SRC4XXX_I2C
+ tristate "Texas Instruments SRC4XXX DIR/DIT and SRC codecs"
+ depends on I2C
+ select SND_SOC_SRC4XXX
+ help
+ Enable support for the TI SRC4XXX family of codecs. These include the
+ scr4392 which has digital receivers, transmitters, and
+ a sample rate converter, including numerous ports.
+
+config SND_SOC_SRC4XXX
+ tristate
+
config SND_SOC_SSM2305
tristate "Analog Devices SSM2305 Class-D Amplifier"
help
@@ -1727,7 +1745,7 @@ config SND_SOC_WCD934X
tristate "WCD9340/WCD9341 Codec"
depends on COMMON_CLK
select SND_SOC_WCD_MBHC
- depends on MFD_WCD934X
+ depends on MFD_WCD934X || COMPILE_TEST
help
The WCD9340/9341 is a audio codec IC Integrated in
Qualcomm SoCs like SDM845.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 92fd441d426a..16a01635dd04 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -100,6 +100,7 @@ snd-soc-dmic-objs := dmic.o
snd-soc-es7134-objs := es7134.o
snd-soc-es7241-objs := es7241.o
snd-soc-es8316-objs := es8316.o
+snd-soc-es8326-objs := es8326.o
snd-soc-es8328-objs := es8328.o
snd-soc-es8328-i2c-objs := es8328-i2c.o
snd-soc-es8328-spi-objs := es8328-spi.o
@@ -231,6 +232,8 @@ snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o
snd-soc-si476x-objs := si476x.o
snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
+snd-soc-src4xxx-objs := src4xxx.o
+snd-soc-src4xxx-i2c-objs := src4xxx-i2c.o
snd-soc-ssm2305-objs := ssm2305.o
snd-soc-ssm2518-objs := ssm2518.o
snd-soc-ssm2602-objs := ssm2602.o
@@ -455,6 +458,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o
obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o
obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
+obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
@@ -579,6 +583,8 @@ obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
+obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o
+obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o
obj-$(CONFIG_SND_SOC_SSM2305) += snd-soc-ssm2305.o
obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index d545a593a251..de1e276bdf7d 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
-#include <linux/kernel.h>
+#include <linux/types.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -37,6 +37,14 @@
#include "cs42l42.h"
#include "cirrus_legacy.h"
+static const char * const cs42l42_supply_names[] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD_FILT",
+ "VL",
+};
+
static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_FRZ_CTL, 0x00 },
{ CS42L42_SRC_CTL, 0x10 },
@@ -395,7 +403,7 @@ static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
u8 val;
- /* all bits of SLOW_START_EN much change together */
+ /* all bits of SLOW_START_EN must change together */
switch (ucontrol->value.integer.value[0]) {
case 0:
val = 0;
@@ -885,22 +893,21 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int channels = params_channels(params);
unsigned int width = (params_width(params) / 8) - 1;
+ unsigned int slot_width = 0;
unsigned int val = 0;
int ret;
cs42l42->srate = params_rate(params);
- cs42l42->bclk = snd_soc_params_to_bclk(params);
-
- /* I2S frame always has 2 channels even for mono audio */
- if (channels == 1)
- cs42l42->bclk *= 2;
/*
* Assume 24-bit samples are in 32-bit slots, to prevent SCLK being
* more than assumed (which would result in overclocking).
*/
if (params_width(params) == 24)
- cs42l42->bclk = (cs42l42->bclk / 3) * 4;
+ slot_width = 32;
+
+ /* I2S frame always has multiple of 2 channels */
+ cs42l42->bclk = snd_soc_tdm_params_to_bclk(params, slot_width, 0, 2);
switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
@@ -2214,6 +2221,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
return ret;
}
+ BUILD_BUG_ON(ARRAY_SIZE(cs42l42_supply_names) != ARRAY_SIZE(cs42l42->supplies));
for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++)
cs42l42->supplies[i].supply = cs42l42_supply_names[i];
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index 5f50970375d4..50299c9f283a 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -12,18 +12,15 @@
#ifndef __CS42L42_H__
#define __CS42L42_H__
+#include <dt-bindings/sound/cs42l42.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/jack.h>
#include <sound/cs42l42.h>
-static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
- "VA",
- "VP",
- "VCP",
- "VD_FILT",
- "VL",
-};
-
struct cs42l42_private {
struct regmap *regmap;
struct device *dev;
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index ca4d47cc9c91..06c6ad3ca2b7 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -1666,10 +1666,9 @@ static int cs43130_show_dc(struct device *dev, char *buf, u8 ch)
struct cs43130_private *cs43130 = i2c_get_clientdata(client);
if (!cs43130->hpload_done)
- return scnprintf(buf, PAGE_SIZE, "NO_HPLOAD\n");
+ return sysfs_emit(buf, "NO_HPLOAD\n");
else
- return scnprintf(buf, PAGE_SIZE, "%u\n",
- cs43130->hpload_dc[ch]);
+ return sysfs_emit(buf, "%u\n", cs43130->hpload_dc[ch]);
}
static ssize_t hpload_dc_l_show(struct device *dev,
@@ -1705,8 +1704,8 @@ static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
if (cs43130->hpload_done && cs43130->ac_meas) {
for (i = 0; i < ARRAY_SIZE(cs43130_ac_freq); i++) {
- tmp = scnprintf(buf + j, PAGE_SIZE - j, "%u\n",
- cs43130->hpload_ac[i][ch]);
+ tmp = sysfs_emit_at(buf, j, "%u\n",
+ cs43130->hpload_ac[i][ch]);
if (!tmp)
break;
@@ -1715,7 +1714,7 @@ static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
return j;
} else {
- return scnprintf(buf, PAGE_SIZE, "NO_HPLOAD\n");
+ return sysfs_emit(buf, "NO_HPLOAD\n");
}
}
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index de7185f73e1e..8643014472ae 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -767,9 +767,31 @@ static void es8316_remove(struct snd_soc_component *component)
clk_disable_unprepare(es8316->mclk);
}
+static int es8316_resume(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8316->regmap, false);
+ regcache_sync(es8316->regmap);
+
+ return 0;
+}
+
+static int es8316_suspend(struct snd_soc_component *component)
+{
+ struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8316->regmap, true);
+ regcache_mark_dirty(es8316->regmap);
+
+ return 0;
+}
+
static const struct snd_soc_component_driver soc_component_dev_es8316 = {
.probe = es8316_probe,
.remove = es8316_remove,
+ .resume = es8316_resume,
+ .suspend = es8316_suspend,
.set_jack = es8316_set_jack,
.controls = es8316_snd_controls,
.num_controls = ARRAY_SIZE(es8316_snd_controls),
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
new file mode 100755
index 000000000000..87c1cc16592b
--- /dev/null
+++ b/sound/soc/codecs/es8326.c
@@ -0,0 +1,905 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8326.c -- es8326 ALSA SoC audio driver
+// Copyright Everest Semiconductor Co., Ltd
+//
+// Authors: David Yang <yangxiaohua@everest-semi.com>
+//
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "es8326.h"
+
+struct es8326_priv {
+ struct clk *mclk;
+ struct i2c_client *i2c;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct delayed_work jack_detect_work;
+ struct delayed_work button_press_work;
+ struct snd_soc_jack *jack;
+ int irq;
+ /* The lock protects the situation that an irq is generated
+ * while enabling or disabling or during an irq.
+ */
+ struct mutex lock;
+ u8 mic1_src;
+ u8 mic2_src;
+ u8 jack_pol;
+ u8 interrupt_src;
+ u8 interrupt_clk;
+ bool jd_inverted;
+ unsigned int sysclk;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0);
+
+static const char *const winsize[] = {
+ "0.25db/2 LRCK",
+ "0.25db/4 LRCK",
+ "0.25db/8 LRCK",
+ "0.25db/16 LRCK",
+ "0.25db/32 LRCK",
+ "0.25db/64 LRCK",
+ "0.25db/128 LRCK",
+ "0.25db/256 LRCK",
+ "0.25db/512 LRCK",
+ "0.25db/1024 LRCK",
+ "0.25db/2048 LRCK",
+ "0.25db/4096 LRCK",
+ "0.25db/8192 LRCK",
+ "0.25db/16384 LRCK",
+ "0.25db/32768 LRCK",
+ "0.25db/65536 LRCK",
+};
+
+static const char *const dacpol_txt[] = {
+ "Normal", "R Invert", "L Invert", "L + R Invert" };
+
+static const struct soc_enum dacpol =
+ SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize);
+static const struct soc_enum drc_winsize =
+ SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize);
+
+static const struct snd_kcontrol_new es8326_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_ENUM("Playback Polarity", dacpol),
+ SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv),
+ SOC_ENUM("DRC Winsize", drc_winsize),
+ SOC_SINGLE_TLV("DRC Target Level", ES8326_DRC_WINSIZE, 0, 0x0f, 0, drc_target_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC Capture Volume", ES8326_ADC1_VOL, ES8326_ADC2_VOL, 0, 0xff, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv),
+ SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv),
+ SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate),
+ SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL,
+ 0, 4, 0, drc_recovery_tlv),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,
+ 0, 0x0f, 0, drc_target_tlv),
+
+};
+
+static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+
+ SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Digital Mute */
+ SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0),
+
+ /* Analog Power Supply*/
+ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1),
+ SND_SOC_DAPM_SUPPLY("Analog Power", ES8326_ANA_PDN, 7, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBias Power", ES8326_ANA_PDN, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref", ES8326_ANA_PDN, 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref", ES8326_ANA_PDN, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref Power", ES8326_ANA_PDN, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", ES8326_ANA_MICBIAS, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", ES8326_ANA_MICBIAS, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0),
+
+ /* Headphone Charge Pump and Output */
+ SND_SOC_DAPM_SUPPLY("HPOR Cal", ES8326_HP_CAL, 7, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("HPOL Cal", ES8326_HP_CAL, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8326_HP_DRIVER,
+ 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Driver Bias", ES8326_HP_DRIVER,
+ 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone LDO", ES8326_HP_DRIVER,
+ 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Reference", ES8326_HP_DRIVER,
+ 0, 1, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOR Supply", ES8326_HP_CAL,
+ ES8326_HPOR_SHIFT, 7, 7, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOL Supply", ES8326_HP_CAL,
+ 0, 7, 7, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
+ {"ADC L1", NULL, "MIC1"},
+ {"ADC R1", NULL, "MIC2"},
+ {"ADC L2", NULL, "MIC3"},
+ {"ADC R2", NULL, "MIC4"},
+
+ {"ADC L", NULL, "ADC L1"},
+ {"ADC R", NULL, "ADC R1"},
+ {"ADC L", NULL, "ADC L2"},
+ {"ADC R", NULL, "ADC R2"},
+
+ {"I2S OUT", NULL, "ADC L"},
+ {"I2S OUT", NULL, "ADC R"},
+
+ {"I2S OUT", NULL, "Analog Power"},
+ {"I2S OUT", NULL, "ADC Vref"},
+ {"I2S OUT", NULL, "Vref Power"},
+ {"I2S OUT", NULL, "IBias Power"},
+ {"I2S IN", NULL, "Analog Power"},
+ {"I2S IN", NULL, "DAC Vref"},
+ {"I2S IN", NULL, "Vref Power"},
+ {"I2S IN", NULL, "IBias Power"},
+
+ {"Right DAC", NULL, "I2S IN"},
+ {"Left DAC", NULL, "I2S IN"},
+
+ {"LHPMIX", NULL, "Left DAC"},
+ {"RHPMIX", NULL, "Right DAC"},
+
+ {"HPOR", NULL, "HPOR Cal"},
+ {"HPOL", NULL, "HPOL Cal"},
+ {"HPOR", NULL, "HPOR Supply"},
+ {"HPOL", NULL, "HPOL Supply"},
+ {"HPOL", NULL, "Headphone Charge Pump"},
+ {"HPOR", NULL, "Headphone Charge Pump"},
+ {"HPOL", NULL, "Headphone Driver Bias"},
+ {"HPOR", NULL, "Headphone Driver Bias"},
+ {"HPOL", NULL, "Headphone LDO"},
+ {"HPOR", NULL, "Headphone LDO"},
+ {"HPOL", NULL, "Headphone Reference"},
+ {"HPOR", NULL, "Headphone Reference"},
+
+ {"HPOL", NULL, "LHPMIX"},
+ {"HPOR", NULL, "RHPMIX"},
+};
+
+static const struct regmap_range es8326_volatile_ranges[] = {
+ regmap_reg_range(ES8326_HP_DETECT, ES8326_HP_DETECT),
+};
+
+static const struct regmap_access_table es8326_volatile_table = {
+ .yes_ranges = es8326_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(es8326_volatile_ranges),
+};
+
+static const struct regmap_config es8326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .volatile_table = &es8326_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 rate;
+ u32 mclk;
+ u8 reg4;
+ u8 reg5;
+ u8 reg6;
+ u8 reg7;
+ u8 reg8;
+ u8 reg9;
+ u8 rega;
+ u8 regb;
+};
+
+/* codec hifi mclk clock divider coefficients */
+/* {ratio, LRCK, MCLK, REG04, REG05, REG06, REG07, REG08, REG09, REG10, REG11} */
+static const struct _coeff_div coeff_div[] = {
+ {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47},
+ {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47},
+ {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {64, 8000, 512000, 0x60, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {64, 44100, 2822400, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x23, 0x47},
+ {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0x38, 0x08, 0x4f, 0x1f},
+ {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {128, 8000, 1024000, 0x60, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {128, 44100, 5644800, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
+ {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {256, 8000, 2048000, 0x60, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {256, 44100, 11289600, 0x00, 0x00, 0x10, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0x00, 0x00, 0x30, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x23, 0x47},
+ {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {400, 48000, 19200000, 0x09, 0x04, 0x0f, 0x6d, 0x3a, 0x0A, 0x4F, 0x1F},
+ {500, 48000, 24000000, 0x18, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+
+ {512, 44100, 22579200, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
+ {1024, 8000, 8192000, 0x60, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+
+ {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+ {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x5F},
+ {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int es8326_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = codec_dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec);
+
+ es8326->sysclk = freq;
+
+ return 0;
+}
+
+static int es8326_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ snd_soc_component_update_bits(component, ES8326_RESET,
+ ES8326_MASTER_MODE_EN, ES8326_MASTER_MODE_EN);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "Codec driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= ES8326_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= ES8326_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= ES8326_DAIFMT_DSP_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DAIFMT_MASK, iface);
+
+ return 0;
+}
+
+static int es8326_pcm_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 es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ u8 srate = 0;
+ int coeff;
+
+ coeff = get_coeff(es8326->sysclk, params_rate(params));
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srate |= ES8326_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srate |= ES8326_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ srate |= ES8326_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srate |= ES8326_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ srate |= ES8326_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface & srate */
+ snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DATA_LEN_MASK, srate);
+
+ if (coeff >= 0) {
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1,
+ coeff_div[coeff].reg4);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV2,
+ coeff_div[coeff].reg5);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL,
+ coeff_div[coeff].reg6);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX,
+ coeff_div[coeff].reg7);
+ regmap_write(es8326->regmap, ES8326_CLK_ADC_SEL,
+ coeff_div[coeff].reg8);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL,
+ coeff_div[coeff].reg9);
+ regmap_write(es8326->regmap, ES8326_CLK_ADC_OSR,
+ coeff_div[coeff].rega);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_OSR,
+ coeff_div[coeff].regb);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8326_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8326->mclk);
+ if (ret)
+ return ret;
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_PWRUP_SEQ_EN);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, 0x45);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
+ (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x02);
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0xAA);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8326->mclk);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x11);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_OFF);
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0xF8);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x08);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_RESET,
+ ES8326_CODEC_RESET | ES8326_PWRUP_SEQ_EN);
+ break;
+ }
+
+ return 0;
+}
+
+#define es8326_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops es8326_ops = {
+ .hw_params = es8326_pcm_hw_params,
+ .set_fmt = es8326_set_dai_fmt,
+ .set_sysclk = es8326_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver es8326_dai = {
+ .name = "ES8326 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = es8326_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = es8326_FORMATS,
+ },
+ .ops = &es8326_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8326_enable_micbias(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void es8326_disable_micbias(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS2");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+/*
+ * For button detection, set the following in soundcard
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ * snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ */
+static void es8326_jack_button_handler(struct work_struct *work)
+{
+ struct es8326_priv *es8326 =
+ container_of(work, struct es8326_priv, button_press_work.work);
+ struct snd_soc_component *comp = es8326->component;
+ unsigned int iface;
+ static int button_to_report, press_count;
+ static int prev_button, cur_button;
+
+ if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */
+ return;
+
+ mutex_lock(&es8326->lock);
+ iface = snd_soc_component_read(comp, ES8326_HP_DETECT);
+ switch (iface) {
+ case 0x93:
+ /* pause button detected */
+ cur_button = SND_JACK_BTN_0;
+ break;
+ case 0x6f:
+ /* button volume up */
+ cur_button = SND_JACK_BTN_1;
+ break;
+ case 0x27:
+ /* button volume down */
+ cur_button = SND_JACK_BTN_2;
+ break;
+ case 0x1e:
+ /* button released or not pressed */
+ cur_button = 0;
+ break;
+ default:
+ break;
+ }
+
+ if ((prev_button == cur_button) && (cur_button != 0)) {
+ press_count++;
+ if (press_count > 10) {
+ /* report a press every 500ms */
+ snd_soc_jack_report(es8326->jack, cur_button,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ press_count = 0;
+ }
+ button_to_report = cur_button;
+ queue_delayed_work(system_wq, &es8326->button_press_work,
+ msecs_to_jiffies(50));
+ } else if (prev_button != cur_button) {
+ /* mismatch, detect again */
+ prev_button = cur_button;
+ queue_delayed_work(system_wq, &es8326->button_press_work,
+ msecs_to_jiffies(50));
+ } else {
+ /* released or no pressed */
+ if (button_to_report != 0) {
+ snd_soc_jack_report(es8326->jack, button_to_report,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ snd_soc_jack_report(es8326->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ button_to_report = 0;
+ }
+ }
+ mutex_unlock(&es8326->lock);
+}
+
+static void es8326_jack_detect_handler(struct work_struct *work)
+{
+ struct es8326_priv *es8326 =
+ container_of(work, struct es8326_priv, jack_detect_work.work);
+ struct snd_soc_component *comp = es8326->component;
+ unsigned int iface;
+
+ mutex_lock(&es8326->lock);
+ iface = snd_soc_component_read(comp, ES8326_HP_DETECT);
+ dev_dbg(comp->dev, "gpio flag %#04x", iface);
+ if ((iface & ES8326_HPINSERT_FLAG) == 0) {
+ /* Jack unplugged or spurious IRQ */
+ dev_dbg(comp->dev, "No headset detected");
+ if (es8326->jack->status & SND_JACK_HEADPHONE) {
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ snd_soc_component_write(comp, ES8326_ADC1_SRC, es8326->mic2_src);
+ es8326_disable_micbias(comp);
+ }
+ } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) {
+ if (es8326->jack->status & SND_JACK_HEADSET) {
+ /* detect button */
+ queue_delayed_work(system_wq, &es8326->button_press_work, 10);
+ } else {
+ if ((iface & ES8326_HPBUTTON_FLAG) == 0x00) {
+ dev_dbg(comp->dev, "Headset detected");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+ snd_soc_component_write(comp,
+ ES8326_ADC1_SRC, es8326->mic1_src);
+ } else {
+ dev_dbg(comp->dev, "Headphone detected");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ }
+ }
+ }
+ mutex_unlock(&es8326->lock);
+}
+
+static irqreturn_t es8326_irq(int irq, void *dev_id)
+{
+ struct es8326_priv *es8326 = dev_id;
+ struct snd_soc_component *comp = es8326->component;
+
+ if (!es8326->jack)
+ goto out;
+
+ es8326_enable_micbias(comp);
+
+ if (es8326->jack->status & SND_JACK_HEADSET)
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(10));
+ else
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(300));
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int es8326_resume(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8326->regmap, false);
+ regcache_sync(es8326->regmap);
+
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ /* Two channel ADC */
+ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x02);
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x1F);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xC8);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x88);
+ regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x20);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x22);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, es8326->mic1_src);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, es8326->mic2_src);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88);
+ regmap_write(es8326->regmap, ES8326_HP_DET,
+ ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, es8326->interrupt_src);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ snd_soc_component_update_bits(component, ES8326_PGAGAIN,
+ ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL);
+
+ regmap_read(es8326->regmap, ES8326_CHIP_VERSION, &reg);
+ if ((reg & ES8326_VERSION_B) == 1) {
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xDD);
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x0F);
+ /* enable button detect */
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xA0);
+ }
+
+ es8326_irq(es8326->irq, es8326);
+ return 0;
+}
+
+static int es8326_suspend(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+ es8326_disable_micbias(component);
+
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF);
+ regcache_cache_only(es8326->regmap, true);
+ regcache_mark_dirty(es8326->regmap);
+
+ return 0;
+}
+
+static int es8326_probe(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8326->component = component;
+ es8326->jd_inverted = device_property_read_bool(component->dev,
+ "everest,jack-detect-inverted");
+
+ ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mic1-src return %d", ret);
+ es8326->mic1_src = ES8326_ADC_AMIC;
+ }
+ dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src);
+
+ ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mic2-src return %d", ret);
+ es8326->mic2_src = ES8326_ADC_DMIC;
+ }
+ dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src);
+
+ ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol);
+ if (ret != 0) {
+ dev_dbg(component->dev, "jack-pol return %d", ret);
+ es8326->jack_pol = ES8326_HP_DET_BUTTON_POL | ES8326_HP_TYPE_OMTP;
+ }
+ dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol);
+
+ ret = device_property_read_u8(component->dev, "everest,interrupt-src", &es8326->jack_pol);
+ if (ret != 0) {
+ dev_dbg(component->dev, "interrupt-src return %d", ret);
+ es8326->interrupt_src = ES8326_HP_DET_SRC_PIN9;
+ }
+ dev_dbg(component->dev, "interrupt-src %x", es8326->interrupt_src);
+
+ ret = device_property_read_u8(component->dev, "everest,interrupt-clk", &es8326->jack_pol);
+ if (ret != 0) {
+ dev_dbg(component->dev, "interrupt-clk return %d", ret);
+ es8326->interrupt_clk = 0x45;
+ }
+ dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
+
+ es8326_resume(component);
+ return 0;
+}
+
+static void es8326_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&es8326->lock);
+ if (es8326->jd_inverted)
+ snd_soc_component_update_bits(component, ES8326_HP_DET,
+ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol);
+ es8326->jack = jack;
+
+ mutex_unlock(&es8326->lock);
+ es8326_irq(es8326->irq, es8326);
+}
+
+static void es8326_disable_jack_detect(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "Enter into %s\n", __func__);
+ if (!es8326->jack)
+ return; /* Already disabled (or never enabled) */
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+
+ mutex_lock(&es8326->lock);
+ if (es8326->jack->status & SND_JACK_MICROPHONE) {
+ es8326_disable_micbias(component);
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ }
+ es8326->jack = NULL;
+ mutex_unlock(&es8326->lock);
+}
+
+static int es8326_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack)
+ es8326_enable_jack_detect(component, jack);
+ else
+ es8326_disable_jack_detect(component);
+
+ return 0;
+}
+
+static void es8326_remove(struct snd_soc_component *component)
+{
+ es8326_disable_jack_detect(component);
+ es8326_set_bias_level(component, SND_SOC_BIAS_OFF);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8326 = {
+ .probe = es8326_probe,
+ .remove = es8326_remove,
+ .resume = es8326_resume,
+ .suspend = es8326_suspend,
+ .set_bias_level = es8326_set_bias_level,
+ .set_jack = es8326_set_jack,
+ .dapm_widgets = es8326_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8326_dapm_widgets),
+ .dapm_routes = es8326_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8326_dapm_routes),
+ .controls = es8326_snd_controls,
+ .num_controls = ARRAY_SIZE(es8326_snd_controls),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int es8326_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct es8326_priv *es8326;
+ int ret;
+
+ es8326 = devm_kzalloc(&i2c->dev, sizeof(struct es8326_priv), GFP_KERNEL);
+ if (!es8326)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, es8326);
+ es8326->i2c = i2c;
+ mutex_init(&es8326->lock);
+ es8326->regmap = devm_regmap_init_i2c(i2c, &es8326_regmap_config);
+ if (IS_ERR(es8326->regmap)) {
+ ret = PTR_ERR(es8326->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ es8326->irq = i2c->irq;
+ INIT_DELAYED_WORK(&es8326->jack_detect_work,
+ es8326_jack_detect_handler);
+ INIT_DELAYED_WORK(&es8326->button_press_work,
+ es8326_jack_button_handler);
+ /* ES8316 is level-based while ES8326 is edge-based */
+ ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "es8326", es8326);
+ if (ret) {
+ dev_warn(&i2c->dev, "Failed to request IRQ: %d: %d\n",
+ es8326->irq, ret);
+ es8326->irq = -ENXIO;
+ }
+
+ es8326->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(es8326->mclk)) {
+ dev_err(&i2c->dev, "unable to get mclk\n");
+ return PTR_ERR(es8326->mclk);
+ }
+ if (!es8326->mclk)
+ dev_warn(&i2c->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8326->mclk);
+ if (ret) {
+ dev_err(&i2c->dev, "unable to enable mclk\n");
+ return ret;
+ }
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_es8326,
+ &es8326_dai, 1);
+}
+
+static const struct i2c_device_id es8326_i2c_id[] = {
+ {"es8326", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, es8326_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8326_of_match[] = {
+ { .compatible = "everest,es8326", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es8326_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8326_acpi_match[] = {
+ {"ESSX8326", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, es8326_acpi_match);
+#endif
+
+static struct i2c_driver es8326_i2c_driver = {
+ .driver = {
+ .name = "es8326",
+ .acpi_match_table = ACPI_PTR(es8326_acpi_match),
+ .of_match_table = of_match_ptr(es8326_of_match),
+ },
+ .probe = es8326_i2c_probe,
+ .id_table = es8326_i2c_id,
+};
+module_i2c_driver(es8326_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8326 driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
new file mode 100755
index 000000000000..8e5ffe5ee10d
--- /dev/null
+++ b/sound/soc/codecs/es8326.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8326.h -- es8326 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#ifndef _ES8326_H
+#define _ES8326_H
+
+#define CONFIG_HHTECH_MINIPMP 1
+
+/* ES8326 register space */
+#define ES8326_RESET 0x00
+#define ES8326_CLK_CTL 0x01
+#define ES8326_CLK_INV 0x02
+#define ES8326_CLK_RESAMPLE 0x03
+#define ES8326_CLK_DIV1 0x04
+#define ES8326_CLK_DIV2 0x05
+#define ES8326_CLK_DLL 0x06
+#define ES8326_CLK_MUX 0x07
+#define ES8326_CLK_ADC_SEL 0x08
+#define ES8326_CLK_DAC_SEL 0x09
+#define ES8326_CLK_ADC_OSR 0x0a
+#define ES8326_CLK_DAC_OSR 0x0b
+#define ES8326_CLK_DIV_CPC 0x0c
+#define ES8326_CLK_DIV_BCLK 0x0d
+#define ES8326_CLK_TRI 0x0e
+#define ES8326_CLK_DIV_LRCK 0x0f
+#define ES8326_CLK_VMIDS1 0x10
+#define ES8326_CLK_VMIDS2 0x11
+#define ES8326_CLK_CAL_TIME 0x12
+#define ES8326_FMT 0x13
+
+#define ES8326_DAC_MUTE 0x14
+#define ES8326_ADC_MUTE 0x15
+#define ES8326_ANA_PDN 0x16
+#define ES8326_PGA_PDN 0x17
+#define ES8326_VMIDSEL 0x18
+#define ES8326_ANA_LP 0x19
+#define ES8326_ANA_DMS 0x1a
+#define ES8326_ANA_MICBIAS 0x1b
+#define ES8326_ANA_VSEL 0x1c
+#define ES8326_SYS_BIAS 0x1d
+#define ES8326_BIAS_SW1 0x1e
+#define ES8326_BIAS_SW2 0x1f
+#define ES8326_BIAS_SW3 0x20
+#define ES8326_BIAS_SW4 0x21
+#define ES8326_VMIDLOW 0x22
+#define ES8326_PGAGAIN 0x23
+#define ES8326_HP_DRIVER 0x24
+#define ES8326_DAC2HPMIX 0x25
+#define ES8326_HP_VOL 0x26
+#define ES8326_HP_CAL 0x27
+#define ES8326_HP_DRIVER_REF 0x28
+#define ES8326_ADC_SCALE 0x29
+#define ES8326_ADC1_SRC 0x2a
+#define ES8326_ADC2_SRC 0x2b
+#define ES8326_ADC1_VOL 0x2c
+#define ES8326_ADC2_VOL 0x2d
+#define ES8326_ADC_RAMPRATE 0x2e
+#define ES8326_ALC_RECOVERY 0x32
+#define ES8326_ALC_LEVEL 0x33
+#define ES8326_ADC_HPFS1 0x34
+#define ES8326_ADC_HPFS2 0x35
+#define ES8326_ADC_EQ 0x36
+#define ES8326_HP_OFFSET_CAL 0x4A
+#define ES8326_HPL_OFFSET_INI 0x4B
+#define ES8326_HPR_OFFSET_INI 0x4C
+#define ES8326_DAC_DSM 0x4D
+#define ES8326_DAC_RAMPRATE 0x4E
+#define ES8326_DAC_VPPSCALE 0x4F
+#define ES8326_DAC_VOL 0x50
+#define ES8326_DRC_RECOVERY 0x53
+#define ES8326_DRC_WINSIZE 0x54
+#define ES8326_HPJACK_TIMER 0x56
+#define ES8326_HP_DET 0x57
+#define ES8326_INT_SOURCE 0x58
+#define ES8326_INTOUT_IO 0x59
+#define ES8326_SDINOUT1_IO 0x5A
+#define ES8326_SDINOUT23_IO 0x5B
+#define ES8326_JACK_PULSE 0x5C
+
+#define ES8326_PULLUP_CTL 0xF9
+#define ES8326_HP_DETECT 0xFB
+#define ES8326_CHIP_ID1 0xFD
+#define ES8326_CHIP_ID2 0xFE
+#define ES8326_CHIP_VERSION 0xFF
+
+/* ES8326_RESET */
+#define ES8326_CSM_ON (1 << 7)
+#define ES8326_MASTER_MODE_EN (1 << 6)
+#define ES8326_PWRUP_SEQ_EN (1 << 5)
+#define ES8326_CODEC_RESET (0x0f << 0)
+#define ES8326_CSM_OFF (0 << 7)
+
+/* ES8326_CLK_CTL */
+#define ES8326_CLK_ON (0x7f << 0)
+#define ES8326_CLK_OFF (0 << 0)
+
+/* ES8326_CLK_INV */
+#define ES8326_BCLK_AS_MCLK (1 << 3)
+
+/* ES8326_FMT */
+#define ES8326_S24_LE (0 << 2)
+#define ES8326_S20_3_LE (1 << 2)
+#define ES8326_S18_LE (2 << 2)
+#define ES8326_S16_LE (3 << 2)
+#define ES8326_S32_LE (4 << 2)
+#define ES8326_DATA_LEN_MASK (7 << 2)
+
+#define ES8326_DAIFMT_MASK ((1 << 5) | (3 << 0))
+#define ES8326_DAIFMT_I2S 0
+#define ES8326_DAIFMT_LEFT_J (1 << 0)
+#define ES8326_DAIFMT_DSP_A (3 << 0)
+#define ES8326_DAIFMT_DSP_B ((1 << 5) | (3 << 0))
+
+/* ES8326_PGAGAIN */
+#define ES8326_MIC_SEL_MASK (3 << 4)
+#define ES8326_MIC1_SEL (1 << 4)
+#define ES8326_MIC2_SEL (1 << 5)
+
+/* ES8326_HP_CAL */
+#define ES8326_HPOR_SHIFT 4
+
+/* ES8326_ADC1_SRC */
+#define ES8326_ADC1_SHIFT 0
+#define ES8326_ADC2_SHIFT 4
+#define ES8326_ADC_SRC_ANA 0
+#define ES8326_ADC_SRC_ANA_INV_SW0 1
+#define ES8326_ADC_SRC_ANA_INV_SW1 2
+#define ES8326_ADC_SRC_DMIC_MCLK 3
+#define ES8326_ADC_SRC_DMIC_SDIN2 4
+#define ES8326_ADC_SRC_DMIC_SDIN2_INV 5
+#define ES8326_ADC_SRC_DMIC_SDIN3 6
+#define ES8326_ADC_SRC_DMIC_SDIN3_INV 7
+
+#define ES8326_ADC_AMIC ((ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC2_SHIFT) \
+ | (ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC1_SHIFT))
+#define ES8326_ADC_DMIC ((ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC2_SHIFT) \
+ | (ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC1_SHIFT))
+/* ES8326_ADC2_SRC */
+#define ES8326_ADC3_SHIFT 0
+#define ES8326_ADC4_SHIFT 3
+
+/* ES8326_HP_DET */
+#define ES8326_HP_DET_SRC_PIN27 (1 << 5)
+#define ES8326_HP_DET_SRC_PIN9 (1 << 4)
+#define ES8326_HP_DET_JACK_POL (1 << 3)
+#define ES8326_HP_DET_BUTTON_POL (1 << 2)
+#define ES8326_HP_TYPE_OMTP (3 << 0)
+#define ES8326_HP_TYPE_CTIA (2 << 0)
+#define ES8326_HP_TYPE_AUTO (1 << 0)
+#define ES8326_HP_TYPE_AUTO_INV (0 << 0)
+
+/* ES8326_SDINOUT1_IO */
+#define ES8326_IO_INPUT (0 << 0)
+#define ES8326_IO_SDIN_SLOT0 (1 << 0)
+#define ES8326_IO_SDIN_SLOT1 (2 << 0)
+#define ES8326_IO_SDIN_SLOT2 (3 << 0)
+#define ES8326_IO_SDIN_SLOT7 (8 << 0)
+#define ES8326_IO_DMIC_CLK (9 << 0)
+#define ES8326_IO_DMIC_CLK_INV (0x0a << 0)
+#define ES8326_IO_SDOUT2 (0x0b << 0)
+#define ES8326_IO_LOW (0x0e << 0)
+#define ES8326_IO_HIGH (0x0f << 0)
+#define ES8326_ADC2DAC (1 << 3)
+#define ES8326_SDINOUT1_SHIFT 4
+
+/* ES8326_SDINOUT23_IO */
+#define ES8326_SDINOUT2_SHIFT 4
+#define ES8326_SDINOUT3_SHIFT 0
+
+/* ES8326_HP_DETECT */
+#define ES8326_HPINSERT_FLAG (1 << 1)
+#define ES8326_HPBUTTON_FLAG (1 << 0)
+
+/* ES8326_CHIP_VERSION 0xFF */
+#define ES8326_VERSION_B (1 << 0)
+
+#endif
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 5435a49604cf..405ec16be2b6 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -474,6 +474,9 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
max98088_mic2pre_get, max98088_mic2pre_set,
max98088_micboost_tlv),
+ SOC_SINGLE("Noise Gate Threshold", M98088_REG_40_MICAGC_THRESH,
+ 4, 15, 0),
+
SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
@@ -1746,7 +1749,6 @@ MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
static int max98088_i2c_probe(struct i2c_client *i2c)
{
struct max98088_priv *max98088;
- int ret;
const struct i2c_device_id *id;
max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
@@ -1769,9 +1771,8 @@ static int max98088_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, max98088);
max98088->pdata = i2c->dev.platform_data;
- ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_max98088,
+ return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_max98088,
&max98088_dai[0], 2);
- return ret;
}
#if defined(CONFIG_OF)
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index 97b64477dde6..899965b19d12 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -281,6 +281,8 @@ static __maybe_unused int max98373_resume(struct device *dev)
msecs_to_jiffies(MAX98373_PROBE_TIMEOUT));
if (!time) {
dev_err(dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c
index c190628e2905..7f624854948c 100644
--- a/sound/soc/codecs/mt6359-accdet.c
+++ b/sound/soc/codecs/mt6359-accdet.c
@@ -965,7 +965,7 @@ static int mt6359_accdet_probe(struct platform_device *pdev)
mutex_init(&priv->res_lock);
priv->accdet_irq = platform_get_irq(pdev, 0);
- if (priv->accdet_irq) {
+ if (priv->accdet_irq >= 0) {
ret = devm_request_threaded_irq(&pdev->dev, priv->accdet_irq,
NULL, mt6359_accdet_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
@@ -979,7 +979,7 @@ static int mt6359_accdet_probe(struct platform_device *pdev)
if (priv->caps & ACCDET_PMIC_EINT0) {
priv->accdet_eint0 = platform_get_irq(pdev, 1);
- if (priv->accdet_eint0) {
+ if (priv->accdet_eint0 >= 0) {
ret = devm_request_threaded_irq(&pdev->dev,
priv->accdet_eint0,
NULL, mt6359_accdet_irq,
@@ -994,7 +994,7 @@ static int mt6359_accdet_probe(struct platform_device *pdev)
}
} else if (priv->caps & ACCDET_PMIC_EINT1) {
priv->accdet_eint1 = platform_get_irq(pdev, 2);
- if (priv->accdet_eint1) {
+ if (priv->accdet_eint1 >= 0) {
ret = devm_request_threaded_irq(&pdev->dev,
priv->accdet_eint1,
NULL, mt6359_accdet_irq,
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index 0be6e72ff5a9..5c29416aa781 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -749,6 +749,8 @@ static int __maybe_unused rt1308_dev_resume(struct device *dev)
msecs_to_jiffies(RT1308_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index e53396606a1c..ed0a11436362 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -734,6 +734,8 @@ static int __maybe_unused rt1316_dev_resume(struct device *dev)
msecs_to_jiffies(RT1316_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 38ab8d4291c2..5a844329800f 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1986,7 +1986,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_PWR_MIXER, 0x0000);
if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
snd_soc_component_write(component, RT5640_PWR_ANLG1,
- 0x0018);
+ 0x2818);
else
snd_soc_component_write(component, RT5640_PWR_ANLG1,
0x0000);
@@ -2600,7 +2600,8 @@ static void rt5640_enable_hda_jack_detect(
snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
- RT5640_PWR_VREF2, RT5640_PWR_VREF2);
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG);
usleep_range(10000, 15000);
snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_FV2, RT5640_PWR_FV2);
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index f04e18c32489..c1a94229dc7e 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -793,6 +793,8 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev)
msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
index f7439e40ca8b..96fc5f36d0d0 100644
--- a/sound/soc/codecs/rt700-sdw.c
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -542,6 +542,8 @@ static int __maybe_unused rt700_dev_resume(struct device *dev)
msecs_to_jiffies(RT700_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
index a085b2f530aa..4120842fe699 100644
--- a/sound/soc/codecs/rt711-sdca-sdw.c
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -449,6 +449,8 @@ static int __maybe_unused rt711_sdca_dev_resume(struct device *dev)
msecs_to_jiffies(RT711_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index 13e731d16675..3f981a9e7fb6 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -244,6 +244,8 @@ static int __maybe_unused rt715_dev_resume(struct device *dev)
msecs_to_jiffies(RT715_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Enumeration not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
index b047bf87a100..4e61e16470ed 100644
--- a/sound/soc/codecs/rt715-sdw.c
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -562,6 +562,8 @@ static int __maybe_unused rt715_dev_resume(struct device *dev)
msecs_to_jiffies(RT715_PROBE_TIMEOUT));
if (!time) {
dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index b992216aee55..3047a6fbb380 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -227,13 +227,11 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
if (!ctrl)
return -ENOMEM;
- name = kzalloc(name_len + 1, GFP_KERNEL);
+ name = kmemdup_nul(ctrl_chunk->name, name_len, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err_free_ctrl;
}
- memcpy(name, ctrl_chunk->name, name_len);
- name[name_len] = '\0';
ctrl->name = name;
/*
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
new file mode 100644
index 000000000000..43daa9dc8ab5
--- /dev/null
+++ b/sound/soc/codecs/src4xxx-i2c.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for SRC4XXX codecs
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt Flax <flatmax@flatmax.com>
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "src4xxx.h"
+
+static int src4xxx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return src4xxx_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &src4xxx_regmap_config), NULL);
+}
+
+static const struct i2c_device_id src4xxx_i2c_ids[] = {
+ { "src4392", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids);
+
+static const struct of_device_id src4xxx_of_match[] = {
+ { .compatible = "ti,src4392", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, src4xxx_of_match);
+
+
+static struct i2c_driver src4xxx_i2c_driver = {
+ .driver = {
+ .name = "src4xxx",
+ .of_match_table = of_match_ptr(src4xxx_of_match),
+ },
+ .probe = src4xxx_i2c_probe,
+ .id_table = src4xxx_i2c_ids,
+};
+module_i2c_driver(src4xxx_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC SRC4392 CODEC I2C driver");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c
new file mode 100644
index 000000000000..db4e280dd055
--- /dev/null
+++ b/sound/soc/codecs/src4xxx.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TI SRC4xxx Audio Codec driver
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt Flax <flatmax@flatmax.com>
+
+#include <linux/module.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "src4xxx.h"
+
+struct src4xxx {
+ struct regmap *regmap;
+ bool master[2];
+ int mclk_hz;
+ struct device *dev;
+};
+
+enum {SRC4XXX_PORTA, SRC4XXX_PORTB};
+
+/* SRC attenuation */
+static const DECLARE_TLV_DB_SCALE(src_tlv, -12750, 50, 0);
+
+static const struct snd_kcontrol_new src4xxx_controls[] = {
+ SOC_DOUBLE_R_TLV("SRC Volume",
+ SRC4XXX_SCR_CTL_30, SRC4XXX_SCR_CTL_31, 0, 255, 1, src_tlv),
+};
+
+/* I2S port control */
+static const char * const port_out_src_text[] = {
+ "loopback", "other_port", "DIR", "SRC"
+};
+static SOC_ENUM_SINGLE_DECL(porta_out_src_enum, SRC4XXX_PORTA_CTL_03, 4,
+ port_out_src_text);
+static SOC_ENUM_SINGLE_DECL(portb_out_src_enum, SRC4XXX_PORTB_CTL_05, 4,
+ port_out_src_text);
+static const struct snd_kcontrol_new porta_out_control =
+ SOC_DAPM_ENUM("Port A source select", porta_out_src_enum);
+static const struct snd_kcontrol_new portb_out_control =
+ SOC_DAPM_ENUM("Port B source select", portb_out_src_enum);
+
+/* Digital audio transmitter control */
+static const char * const dit_mux_text[] = {"Port A", "Port B", "DIR", "SRC"};
+static SOC_ENUM_SINGLE_DECL(dit_mux_enum, SRC4XXX_TX_CTL_07, 3, dit_mux_text);
+static const struct snd_kcontrol_new dit_mux_control =
+ SOC_DAPM_ENUM("DIT source", dit_mux_enum);
+
+/* SRC control */
+static const char * const src_in_text[] = {"Port A", "Port B", "DIR"};
+static SOC_ENUM_SINGLE_DECL(src_in_enum, SRC4XXX_SCR_CTL_2D, 0, src_in_text);
+static const struct snd_kcontrol_new src_in_control =
+ SOC_DAPM_ENUM("SRC source select", src_in_enum);
+
+/* DIR control */
+static const char * const dir_in_text[] = {"Ch 1", "Ch 2", "Ch 3", "Ch 4"};
+static SOC_ENUM_SINGLE_DECL(dir_in_enum, SRC4XXX_RCV_CTL_0D, 0, dir_in_text);
+static const struct snd_kcontrol_new dir_in_control =
+ SOC_DAPM_ENUM("Digital Input", dir_in_enum);
+
+static const struct snd_soc_dapm_widget src4xxx_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("loopback_A"),
+ SND_SOC_DAPM_INPUT("other_port_A"),
+ SND_SOC_DAPM_INPUT("DIR_A"),
+ SND_SOC_DAPM_INPUT("SRC_A"),
+ SND_SOC_DAPM_MUX("Port A source",
+ SND_SOC_NOPM, 0, 0, &porta_out_control),
+
+ SND_SOC_DAPM_INPUT("loopback_B"),
+ SND_SOC_DAPM_INPUT("other_port_B"),
+ SND_SOC_DAPM_INPUT("DIR_B"),
+ SND_SOC_DAPM_INPUT("SRC_B"),
+ SND_SOC_DAPM_MUX("Port B source",
+ SND_SOC_NOPM, 0, 0, &portb_out_control),
+
+ SND_SOC_DAPM_INPUT("Port_A"),
+ SND_SOC_DAPM_INPUT("Port_B"),
+ SND_SOC_DAPM_INPUT("DIR_"),
+
+ /* Digital audio receivers and transmitters */
+ SND_SOC_DAPM_OUTPUT("DIR_OUT"),
+ SND_SOC_DAPM_OUTPUT("SRC_OUT"),
+ SND_SOC_DAPM_MUX("DIT Out Src", SRC4XXX_PWR_RST_01,
+ SRC4XXX_ENABLE_DIT_SHIFT, 1, &dit_mux_control),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF_A_RX", "Playback A", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF_A_TX", "Capture A", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1),
+ SND_SOC_DAPM_AIF_IN("AIF_B_RX", "Playback B", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("AIF_B_TX", "Capture B", 0,
+ SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1),
+
+ SND_SOC_DAPM_MUX("SRC source", SND_SOC_NOPM, 0, 0, &src_in_control),
+
+ SND_SOC_DAPM_INPUT("MCLK"),
+ SND_SOC_DAPM_INPUT("RXMCLKI"),
+ SND_SOC_DAPM_INPUT("RXMCLKO"),
+
+ SND_SOC_DAPM_INPUT("RX1"),
+ SND_SOC_DAPM_INPUT("RX2"),
+ SND_SOC_DAPM_INPUT("RX3"),
+ SND_SOC_DAPM_INPUT("RX4"),
+ SND_SOC_DAPM_MUX("Digital Input", SRC4XXX_PWR_RST_01,
+ SRC4XXX_ENABLE_DIR_SHIFT, 1, &dir_in_control),
+};
+
+static const struct snd_soc_dapm_route src4xxx_audio_routes[] = {
+ /* I2S Input to Output Routing */
+ {"Port A source", "loopback", "loopback_A"},
+ {"Port A source", "other_port", "other_port_A"},
+ {"Port A source", "DIR", "DIR_A"},
+ {"Port A source", "SRC", "SRC_A"},
+ {"Port B source", "loopback", "loopback_B"},
+ {"Port B source", "other_port", "other_port_B"},
+ {"Port B source", "DIR", "DIR_B"},
+ {"Port B source", "SRC", "SRC_B"},
+ /* DIT muxing */
+ {"DIT Out Src", "Port A", "Capture A"},
+ {"DIT Out Src", "Port B", "Capture B"},
+ {"DIT Out Src", "DIR", "DIR_OUT"},
+ {"DIT Out Src", "SRC", "SRC_OUT"},
+
+ /* SRC input selection */
+ {"SRC source", "Port A", "Port_A"},
+ {"SRC source", "Port B", "Port_B"},
+ {"SRC source", "DIR", "DIR_"},
+ /* SRC mclk selection */
+ {"SRC mclk source", "Master (MCLK)", "MCLK"},
+ {"SRC mclk source", "Master (RXCLKI)", "RXMCLKI"},
+ {"SRC mclk source", "Recovered receiver clk", "RXMCLKO"},
+ /* DIR input selection */
+ {"Digital Input", "Ch 1", "RX1"},
+ {"Digital Input", "Ch 2", "RX2"},
+ {"Digital Input", "Ch 3", "RX3"},
+ {"Digital Input", "Ch 4", "RX4"},
+};
+
+
+static const struct snd_soc_component_driver src4xxx_driver = {
+ .controls = src4xxx_controls,
+ .num_controls = ARRAY_SIZE(src4xxx_controls),
+
+ .dapm_widgets = src4xxx_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(src4xxx_dapm_widgets),
+ .dapm_routes = src4xxx_audio_routes,
+ .num_dapm_routes = ARRAY_SIZE(src4xxx_audio_routes),
+};
+
+static int src4xxx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl = SRC4XXX_BUS_MASTER;
+ src4xxx->master[dai->id] = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ctrl = 0;
+ src4xxx->master[dai->id] = false;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl |= SRC4XXX_BUS_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl |= SRC4XXX_BUS_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl |= SRC4XXX_BUS_RIGHT_J_24;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ regmap_update_bits(src4xxx->regmap, SRC4XXX_BUS_FMT(dai->id),
+ SRC4XXX_BUS_FMT_MS_MASK, ctrl);
+
+ return 0;
+}
+
+static int src4xxx_set_mclk_hz(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+
+ dev_info(component->dev, "changing mclk rate from %d to %d Hz\n",
+ src4xxx->mclk_hz, freq);
+ src4xxx->mclk_hz = freq;
+
+ return 0;
+}
+
+static int src4xxx_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 src4xxx *src4xxx = snd_soc_component_get_drvdata(component);
+ unsigned int mclk_div;
+ int val, pj, jd, d;
+ int reg;
+ int ret;
+
+ switch (dai->id) {
+ case SRC4XXX_PORTB:
+ reg = SRC4XXX_PORTB_CTL_06;
+ break;
+ default:
+ reg = SRC4XXX_PORTA_CTL_04;
+ break;
+ }
+
+ if (src4xxx->master[dai->id]) {
+ mclk_div = src4xxx->mclk_hz/params_rate(params);
+ if (src4xxx->mclk_hz != mclk_div*params_rate(params)) {
+ dev_err(component->dev,
+ "mclk %d / rate %d has a remainder.\n",
+ src4xxx->mclk_hz, params_rate(params));
+ return -EINVAL;
+ }
+
+ val = ((int)mclk_div - 128) / 128;
+ if ((val < 0) | (val > 3)) {
+ dev_err(component->dev,
+ "div register setting %d is out of range\n",
+ val);
+ dev_err(component->dev,
+ "unsupported sample rate %d Hz for the master clock of %d Hz\n",
+ params_rate(params), src4xxx->mclk_hz);
+ return -EINVAL;
+ }
+
+ /* set the TX DIV */
+ ret = regmap_update_bits(src4xxx->regmap,
+ SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ /* set the PLL for the digital receiver */
+ switch (src4xxx->mclk_hz) {
+ case 24576000:
+ pj = 0x22;
+ jd = 0x00;
+ d = 0x00;
+ break;
+ case 22579200:
+ pj = 0x22;
+ jd = 0x1b;
+ d = 0xa3;
+ break;
+ default:
+ /* don't error out here,
+ * other parts of the chip are still functional
+ * Dummy initialize variables to avoid
+ * -Wsometimes-uninitialized from clang.
+ */
+ dev_info(component->dev,
+ "Couldn't set the RCV PLL as this master clock rate is unknown. Chosen regmap values may not match real world values.\n");
+ pj = 0x0;
+ jd = 0xff;
+ d = 0xff;
+ break;
+ }
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_0F, pj);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_0F);
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_10, jd);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_10);
+ ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_11, d);
+ if (ret < 0)
+ dev_err(component->dev,
+ "Failed to update PLL register 0x%x\n",
+ SRC4XXX_RCV_PLL_11);
+
+ ret = regmap_update_bits(src4xxx->regmap,
+ SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ return regmap_update_bits(src4xxx->regmap, reg,
+ SRC4XXX_MCLK_DIV_MASK, val);
+ } else {
+ dev_info(dai->dev, "not setting up MCLK as not master\n");
+ }
+
+ return 0;
+};
+
+static const struct snd_soc_dai_ops src4xxx_dai_ops = {
+ .hw_params = src4xxx_hw_params,
+ .set_sysclk = src4xxx_set_mclk_hz,
+ .set_fmt = src4xxx_set_dai_fmt,
+};
+
+#define SRC4XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#define SRC4XXX_RATES (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 struct snd_soc_dai_driver src4xxx_dai_driver[] = {
+ {
+ .id = SRC4XXX_PORTA,
+ .name = "src4xxx-portA",
+ .playback = {
+ .stream_name = "Playback A",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture A",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .ops = &src4xxx_dai_ops,
+ },
+ {
+ .id = SRC4XXX_PORTB,
+ .name = "src4xxx-portB",
+ .playback = {
+ .stream_name = "Playback B",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture B",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SRC4XXX_RATES,
+ .formats = SRC4XXX_FORMATS,
+ },
+ .ops = &src4xxx_dai_ops,
+ },
+};
+
+static const struct reg_default src4xxx_reg_defaults[] = {
+ { SRC4XXX_PWR_RST_01, 0x00 }, /* all powered down intially */
+ { SRC4XXX_PORTA_CTL_03, 0x00 },
+ { SRC4XXX_PORTA_CTL_04, 0x00 },
+ { SRC4XXX_PORTB_CTL_05, 0x00 },
+ { SRC4XXX_PORTB_CTL_06, 0x00 },
+ { SRC4XXX_TX_CTL_07, 0x00 },
+ { SRC4XXX_TX_CTL_08, 0x00 },
+ { SRC4XXX_TX_CTL_09, 0x00 },
+ { SRC4XXX_SRC_DIT_IRQ_MSK_0B, 0x00 },
+ { SRC4XXX_SRC_DIT_IRQ_MODE_0C, 0x00 },
+ { SRC4XXX_RCV_CTL_0D, 0x00 },
+ { SRC4XXX_RCV_CTL_0E, 0x00 },
+ { SRC4XXX_RCV_PLL_0F, 0x00 }, /* not spec. in the datasheet */
+ { SRC4XXX_RCV_PLL_10, 0xff }, /* not spec. in the datasheet */
+ { SRC4XXX_RCV_PLL_11, 0xff }, /* not spec. in the datasheet */
+ { SRC4XXX_RVC_IRQ_MSK_16, 0x00 },
+ { SRC4XXX_RVC_IRQ_MSK_17, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_18, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_19, 0x00 },
+ { SRC4XXX_RVC_IRQ_MODE_1A, 0x00 },
+ { SRC4XXX_GPIO_1_1B, 0x00 },
+ { SRC4XXX_GPIO_2_1C, 0x00 },
+ { SRC4XXX_GPIO_3_1D, 0x00 },
+ { SRC4XXX_GPIO_4_1E, 0x00 },
+ { SRC4XXX_SCR_CTL_2D, 0x00 },
+ { SRC4XXX_SCR_CTL_2E, 0x00 },
+ { SRC4XXX_SCR_CTL_2F, 0x00 },
+ { SRC4XXX_SCR_CTL_30, 0x00 },
+ { SRC4XXX_SCR_CTL_31, 0x00 },
+};
+
+int src4xxx_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev))
+{
+ struct src4xxx *src4xxx;
+ int ret;
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ src4xxx = devm_kzalloc(dev, sizeof(*src4xxx), GFP_KERNEL);
+ if (!src4xxx)
+ return -ENOMEM;
+
+ src4xxx->regmap = regmap;
+ src4xxx->dev = dev;
+ src4xxx->mclk_hz = 0; /* mclk has not been configured yet */
+ dev_set_drvdata(dev, src4xxx);
+
+ ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_RESET);
+ if (ret < 0)
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
+ usleep_range(1, 500); /* sleep for more then 500 ns */
+ ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_POWER_DOWN);
+ if (ret < 0)
+ dev_err(dev, "Failed to decommission reset: %d\n", ret);
+ usleep_range(500, 1000); /* sleep for 500 us or more */
+
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_PWR_RST_01,
+ SRC4XXX_POWER_ENABLE, SRC4XXX_POWER_ENABLE);
+ if (ret < 0)
+ dev_err(dev, "Failed to port A and B : %d\n", ret);
+
+ /* set receiver to use master clock (rcv mclk is most likely jittery) */
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0D,
+ SRC4XXX_RXCLK_MCLK, SRC4XXX_RXCLK_MCLK);
+ if (ret < 0)
+ dev_err(dev,
+ "Failed to enable mclk as the PLL1 DIR reference : %d\n", ret);
+
+ /* default to leaving the PLL2 running on loss of lock, divide by 8 */
+ ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0E,
+ SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL,
+ SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL);
+ if (ret < 0)
+ dev_err(dev, "Failed to enable mclk rec and div : %d\n", ret);
+
+ ret = devm_snd_soc_register_component(dev, &src4xxx_driver,
+ src4xxx_dai_driver, ARRAY_SIZE(src4xxx_dai_driver));
+ if (ret == 0)
+ dev_info(dev, "src4392 probe ok %d\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(src4xxx_probe);
+
+static bool src4xxx_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SRC4XXX_RES_00:
+ case SRC4XXX_GLOBAL_ITR_STS_02:
+ case SRC4XXX_SRC_DIT_STS_0A:
+ case SRC4XXX_NON_AUDIO_D_12:
+ case SRC4XXX_RVC_STS_13:
+ case SRC4XXX_RVC_STS_14:
+ case SRC4XXX_RVC_STS_15:
+ case SRC4XXX_SUB_CODE_1F:
+ case SRC4XXX_SUB_CODE_20:
+ case SRC4XXX_SUB_CODE_21:
+ case SRC4XXX_SUB_CODE_22:
+ case SRC4XXX_SUB_CODE_23:
+ case SRC4XXX_SUB_CODE_24:
+ case SRC4XXX_SUB_CODE_25:
+ case SRC4XXX_SUB_CODE_26:
+ case SRC4XXX_SUB_CODE_27:
+ case SRC4XXX_SUB_CODE_28:
+ case SRC4XXX_PC_PREAMBLE_HI_29:
+ case SRC4XXX_PC_PREAMBLE_LO_2A:
+ case SRC4XXX_PD_PREAMBLE_HI_2B:
+ case SRC4XXX_PC_PREAMBLE_LO_2C:
+ case SRC4XXX_IO_RATIO_32:
+ case SRC4XXX_IO_RATIO_33:
+ return true;
+ }
+
+ if (reg > SRC4XXX_IO_RATIO_33 && reg < SRC4XXX_PAGE_SEL_7F)
+ return true;
+
+ return false;
+}
+
+const struct regmap_config src4xxx_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = SRC4XXX_IO_RATIO_33,
+
+ .reg_defaults = src4xxx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(src4xxx_reg_defaults),
+ .volatile_reg = src4xxx_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(src4xxx_regmap_config);
+
+MODULE_DESCRIPTION("ASoC SRC4XXX CODEC driver");
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.h b/sound/soc/codecs/src4xxx.h
new file mode 100644
index 000000000000..5bf778fb9945
--- /dev/null
+++ b/sound/soc/codecs/src4xxx.h
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// src4xxx.h -- SRC4XXX ALSA SoC audio driver
+//
+// Copyright 2021-2022 Deqx Pty Ltd
+// Author: Matt R Flax <flatmax@flatmax.com>
+
+#ifndef __SRC4XXX_H__
+#define __SRC4XXX_H__
+
+#define SRC4XXX_RES_00 0x00
+#define SRC4XXX_PWR_RST_01 0x01
+#define SRC4XXX_RESET 0x80
+#define SRC4XXX_POWER_DOWN 0x00
+#define SRC4XXX_POWER_ENABLE 0x20
+#define SRC4XXX_ENABLE_SRC 0x1
+#define SRC4XXX_ENABLE_SRC_SHIFT 0
+#define SRC4XXX_ENABLE_DIR 0x2
+#define SRC4XXX_ENABLE_DIR_SHIFT 1
+#define SRC4XXX_ENABLE_DIT 0x4
+#define SRC4XXX_ENABLE_DIT_SHIFT 2
+#define SRC4XXX_ENABLE_PORT_B 0x8
+#define SRC4XXX_ENABLE_PORT_B_SHIFT 3
+#define SRC4XXX_ENABLE_PORT_A 0x10
+#define SRC4XXX_ENABLE_PORT_A_SHIFT 4
+
+#define SRC4XXX_PORTA_CTL_03 0x03
+#define SRC4XXX_BUS_MASTER 0x8
+#define SRC4XXX_BUS_LEFT_J 0x0
+#define SRC4XXX_BUS_I2S 0x1
+#define SRC4XXX_BUS_RIGHT_J_16 0x4
+#define SRC4XXX_BUS_RIGHT_J_18 0x5
+#define SRC4XXX_BUS_RIGHT_J_20 0x6
+#define SRC4XXX_BUS_RIGHT_J_24 0x7
+#define SRC4XXX_BUS_FMT_MS_MASK 0xf
+
+#define SRC4XXX_PORTA_CTL_04 0x04
+#define SRC4XXX_MCLK_DIV_MASK 0x3
+
+#define SRC4XXX_BUS_FMT(id) (SRC4XXX_PORTA_CTL_03+2*id)
+#define SRC4XXX_BUS_CLK(id) (SRC4XXX_PORTA_CTL_04+2*id)
+
+#define SRC4XXX_PORTB_CTL_05 0x05
+#define SRC4XXX_PORTB_CTL_06 0x06
+
+#define SRC4XXX_TX_CTL_07 0x07
+#define SRC4XXX_TX_MCLK_DIV_MASK 0x60
+#define SRC4XXX_TX_MCLK_DIV_SHIFT 5
+
+#define SRC4XXX_TX_CTL_08 0x08
+#define SRC4XXX_TX_CTL_09 0x09
+#define SRC4XXX_SRC_DIT_IRQ_MSK_0B 0x0B
+#define SRC4XXX_SRC_BTI_EN 0x01
+#define SRC4XXX_SRC_TSLIP_EN 0x02
+#define SRC4XXX_SRC_DIT_IRQ_MODE_0C 0x0C
+#define SRC4XXX_RCV_CTL_0D 0x0D
+#define SRC4XXX_RXCLK_RXCKI 0x0
+#define SRC4XXX_RXCLK_MCLK 0x8
+#define SRC4XXX_RCV_CTL_0E 0x0E
+#define SRC4XXX_REC_MCLK_EN 0x1
+#define SRC4XXX_PLL2_DIV_0 (0x0<<1)
+#define SRC4XXX_PLL2_DIV_2 (0x1<<1)
+#define SRC4XXX_PLL2_DIV_4 (0x2<<1)
+#define SRC4XXX_PLL2_DIV_8 (0x3<<1)
+#define SRC4XXX_PLL2_LOL 0x8
+#define SRC4XXX_RCV_PLL_0F 0x0F
+#define SRC4XXX_RCV_PLL_10 0x10
+#define SRC4XXX_RCV_PLL_11 0x11
+#define SRC4XXX_RVC_IRQ_MSK_16 0x16
+#define SRC4XXX_RVC_IRQ_MSK_17 0x17
+#define SRC4XXX_RVC_IRQ_MODE_18 0x18
+#define SRC4XXX_RVC_IRQ_MODE_19 0x19
+#define SRC4XXX_RVC_IRQ_MODE_1A 0x1A
+#define SRC4XXX_GPIO_1_1B 0x1B
+#define SRC4XXX_GPIO_2_1C 0x1C
+#define SRC4XXX_GPIO_3_1D 0x1D
+#define SRC4XXX_GPIO_4_1E 0x1E
+#define SRC4XXX_SCR_CTL_2D 0x2D
+#define SRC4XXX_SCR_CTL_2E 0x2E
+#define SRC4XXX_SCR_CTL_2F 0x2F
+#define SRC4XXX_SCR_CTL_30 0x30
+#define SRC4XXX_SCR_CTL_31 0x31
+#define SRC4XXX_PAGE_SEL_7F 0x7F
+
+// read only registers
+#define SRC4XXX_GLOBAL_ITR_STS_02 0x02
+#define SRC4XXX_SRC_DIT_STS_0A 0x0A
+#define SRC4XXX_NON_AUDIO_D_12 0x12
+#define SRC4XXX_RVC_STS_13 0x13
+#define SRC4XXX_RVC_STS_14 0x14
+#define SRC4XXX_RVC_STS_15 0x15
+#define SRC4XXX_SUB_CODE_1F 0x1F
+#define SRC4XXX_SUB_CODE_20 0x20
+#define SRC4XXX_SUB_CODE_21 0x21
+#define SRC4XXX_SUB_CODE_22 0x22
+#define SRC4XXX_SUB_CODE_23 0x23
+#define SRC4XXX_SUB_CODE_24 0x24
+#define SRC4XXX_SUB_CODE_25 0x25
+#define SRC4XXX_SUB_CODE_26 0x26
+#define SRC4XXX_SUB_CODE_27 0x27
+#define SRC4XXX_SUB_CODE_28 0x28
+#define SRC4XXX_PC_PREAMBLE_HI_29 0x29
+#define SRC4XXX_PC_PREAMBLE_LO_2A 0x2A
+#define SRC4XXX_PD_PREAMBLE_HI_2B 0x2B
+#define SRC4XXX_PC_PREAMBLE_LO_2C 0x2C
+#define SRC4XXX_IO_RATIO_32 0x32
+#define SRC4XXX_IO_RATIO_33 0x33
+
+int src4xxx_probe(struct device *dev, struct regmap *regmap,
+ void (*switch_mode)(struct device *dev));
+extern const struct regmap_config src4xxx_regmap_config;
+
+#endif /* __SRC4XXX_H__ */
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 846d9d3ecc9d..51b87a936179 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -31,11 +31,66 @@ struct tas2764_priv {
struct gpio_desc *sdz_gpio;
struct regmap *regmap;
struct device *dev;
+ int irq;
int v_sense_slot;
int i_sense_slot;
+
+ bool dac_powered;
+ bool unmuted;
+};
+
+static const char *tas2764_int_ltch0_msgs[8] = {
+ "fault: over temperature", /* INT_LTCH0 & BIT(0) */
+ "fault: over current",
+ "fault: bad TDM clock",
+ "limiter active",
+ "fault: PVDD below limiter inflection point",
+ "fault: limiter max attenuation",
+ "fault: BOP infinite hold",
+ "fault: BOP mute", /* INT_LTCH0 & BIT(7) */
+};
+
+static const unsigned int tas2764_int_readout_regs[6] = {
+ TAS2764_INT_LTCH0,
+ TAS2764_INT_LTCH1,
+ TAS2764_INT_LTCH1_0,
+ TAS2764_INT_LTCH2,
+ TAS2764_INT_LTCH3,
+ TAS2764_INT_LTCH4,
};
+static irqreturn_t tas2764_irq(int irq, void *data)
+{
+ struct tas2764_priv *tas2764 = data;
+ u8 latched[6] = {0, 0, 0, 0, 0, 0};
+ int ret = IRQ_NONE;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(latched); i++)
+ latched[i] = snd_soc_component_read(tas2764->component,
+ tas2764_int_readout_regs[i]);
+
+ for (i = 0; i < 8; i++) {
+ if (latched[0] & BIT(i)) {
+ dev_crit_ratelimited(tas2764->dev, "%s\n",
+ tas2764_int_ltch0_msgs[i]);
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if (latched[0]) {
+ dev_err_ratelimited(tas2764->dev, "other context to the fault: %02x,%02x,%02x,%02x,%02x",
+ latched[1], latched[2], latched[3], latched[4], latched[5]);
+ snd_soc_component_update_bits(tas2764->component,
+ TAS2764_INT_CLK_CFG,
+ TAS2764_INT_CLK_CFG_IRQZ_CLR,
+ TAS2764_INT_CLK_CFG_IRQZ_CLR);
+ }
+
+ return ret;
+}
+
static void tas2764_reset(struct tas2764_priv *tas2764)
{
if (tas2764->reset_gpio) {
@@ -50,34 +105,22 @@ static void tas2764_reset(struct tas2764_priv *tas2764)
usleep_range(1000, 2000);
}
-static int tas2764_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
+static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764)
{
- struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_component *component = tas2764->component;
+ unsigned int val;
+ int ret;
- switch (level) {
- case SND_SOC_BIAS_ON:
- snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_ACTIVE);
- break;
- case SND_SOC_BIAS_STANDBY:
- case SND_SOC_BIAS_PREPARE:
- snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_MUTE);
- break;
- case SND_SOC_BIAS_OFF:
- snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_SHUTDOWN);
- break;
+ if (tas2764->dac_powered)
+ val = tas2764->unmuted ?
+ TAS2764_PWR_CTRL_ACTIVE : TAS2764_PWR_CTRL_MUTE;
+ else
+ val = TAS2764_PWR_CTRL_SHUTDOWN;
- default:
- dev_err(tas2764->dev,
- "wrong power level setting %d\n", level);
- return -EINVAL;
- }
+ ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK, val);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -114,9 +157,7 @@ static int tas2764_codec_resume(struct snd_soc_component *component)
usleep_range(1000, 2000);
}
- ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_ACTIVE);
+ ret = tas2764_update_pwr_ctrl(tas2764);
if (ret < 0)
return ret;
@@ -150,14 +191,12 @@ static int tas2764_dac_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_MUTE);
+ tas2764->dac_powered = true;
+ ret = tas2764_update_pwr_ctrl(tas2764);
break;
case SND_SOC_DAPM_PRE_PMD:
- ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_SHUTDOWN);
+ tas2764->dac_powered = false;
+ ret = tas2764_update_pwr_ctrl(tas2764);
break;
default:
dev_err(tas2764->dev, "Unsupported event\n");
@@ -202,17 +241,11 @@ static const struct snd_soc_dapm_route tas2764_audio_map[] = {
static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_component *component = dai->component;
- int ret;
-
- ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- mute ? TAS2764_PWR_CTRL_MUTE : 0);
+ struct tas2764_priv *tas2764 =
+ snd_soc_component_get_drvdata(dai->component);
- if (ret < 0)
- return ret;
-
- return 0;
+ tas2764->unmuted = !mute;
+ return tas2764_update_pwr_ctrl(tas2764);
}
static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth)
@@ -485,7 +518,7 @@ static struct snd_soc_dai_driver tas2764_dai_driver[] = {
.id = 0,
.playback = {
.stream_name = "ASI1 Playback",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = TAS2764_RATES,
.formats = TAS2764_FORMATS,
@@ -516,6 +549,34 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
tas2764_reset(tas2764);
+ if (tas2764->irq) {
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK1, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK2, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK3, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK4, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "tas2764", tas2764);
+ if (ret)
+ dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret);
+ }
+
ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
TAS2764_TDM_CFG5_VSNS_ENABLE, 0);
if (ret < 0)
@@ -526,30 +587,33 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
if (ret < 0)
return ret;
- ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
- TAS2764_PWR_CTRL_MASK,
- TAS2764_PWR_CTRL_MUTE);
- if (ret < 0)
- return ret;
-
return 0;
}
static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0);
static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1);
+static const char * const tas2764_hpf_texts[] = {
+ "Disabled", "2 Hz", "50 Hz", "100 Hz", "200 Hz",
+ "400 Hz", "800 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2764_hpf_enum, TAS2764_DC_BLK0,
+ TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts);
+
static const struct snd_kcontrol_new tas2764_snd_controls[] = {
SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0,
TAS2764_DVC_MAX, 1, tas2764_playback_volume),
SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0,
tas2764_digital_tlv),
+ SOC_ENUM("HPF Corner Frequency", tas2764_hpf_enum),
};
static const struct snd_soc_component_driver soc_component_driver_tas2764 = {
.probe = tas2764_codec_probe,
.suspend = tas2764_codec_suspend,
.resume = tas2764_codec_resume,
- .set_bias_level = tas2764_set_bias_level,
.controls = tas2764_snd_controls,
.num_controls = ARRAY_SIZE(tas2764_snd_controls),
.dapm_widgets = tas2764_dapm_widgets,
@@ -585,9 +649,21 @@ static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
},
};
+static bool tas2764_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:
+ case TAS2764_INT_CLK_CFG:
+ return true;
+ default:
+ return false;
+ }
+}
+
static const struct regmap_config tas2764_i2c_regmap = {
.reg_bits = 8,
.val_bits = 8,
+ .volatile_reg = tas2764_volatile_register,
.reg_defaults = tas2764_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults),
.cache_type = REGCACHE_RBTREE,
@@ -641,6 +717,7 @@ static int tas2764_i2c_probe(struct i2c_client *client)
return -ENOMEM;
tas2764->dev = &client->dev;
+ tas2764->irq = client->irq;
i2c_set_clientdata(client, tas2764);
dev_set_drvdata(&client->dev, tas2764);
diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h
index f015f22a083b..168af772a898 100644
--- a/sound/soc/codecs/tas2764.h
+++ b/sound/soc/codecs/tas2764.h
@@ -33,6 +33,10 @@
#define TAS2764_VSENSE_POWER_EN 3
#define TAS2764_ISENSE_POWER_EN 4
+/* DC Blocker Control */
+#define TAS2764_DC_BLK0 TAS2764_REG(0x0, 0x04)
+#define TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT 0
+
/* Digital Volume Control */
#define TAS2764_DVC TAS2764_REG(0X0, 0x1a)
#define TAS2764_DVC_MAX 0xc9
@@ -87,4 +91,23 @@
#define TAS2764_TDM_CFG6_ISNS_ENABLE BIT(6)
#define TAS2764_TDM_CFG6_50_MASK GENMASK(5, 0)
+/* Interrupt Masks */
+#define TAS2764_INT_MASK0 TAS2764_REG(0x0, 0x3b)
+#define TAS2764_INT_MASK1 TAS2764_REG(0x0, 0x3c)
+#define TAS2764_INT_MASK2 TAS2764_REG(0x0, 0x40)
+#define TAS2764_INT_MASK3 TAS2764_REG(0x0, 0x41)
+#define TAS2764_INT_MASK4 TAS2764_REG(0x0, 0x3d)
+
+/* Latched Fault Registers */
+#define TAS2764_INT_LTCH0 TAS2764_REG(0x0, 0x49)
+#define TAS2764_INT_LTCH1 TAS2764_REG(0x0, 0x4a)
+#define TAS2764_INT_LTCH1_0 TAS2764_REG(0x0, 0x4b)
+#define TAS2764_INT_LTCH2 TAS2764_REG(0x0, 0x4f)
+#define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50)
+#define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51)
+
+/* Clock/IRQ Settings */
+#define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c)
+#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2)
+
#endif /* __TAS2764__ */
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index 3cb634c28261..bb653b664146 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -46,34 +46,22 @@ static void tas2770_reset(struct tas2770_priv *tas2770)
usleep_range(1000, 2000);
}
-static int tas2770_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
+static int tas2770_update_pwr_ctrl(struct tas2770_priv *tas2770)
{
- struct tas2770_priv *tas2770 =
- snd_soc_component_get_drvdata(component);
+ struct snd_soc_component *component = tas2770->component;
+ unsigned int val;
+ int ret;
- 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_STANDBY:
- case SND_SOC_BIAS_PREPARE:
- snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
- TAS2770_PWR_CTRL_MASK,
- TAS2770_PWR_CTRL_MUTE);
- break;
- case SND_SOC_BIAS_OFF:
- snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
- TAS2770_PWR_CTRL_MASK,
- TAS2770_PWR_CTRL_SHUTDOWN);
- break;
+ if (tas2770->dac_powered)
+ val = tas2770->unmuted ?
+ TAS2770_PWR_CTRL_ACTIVE : TAS2770_PWR_CTRL_MUTE;
+ else
+ val = TAS2770_PWR_CTRL_SHUTDOWN;
- default:
- dev_err(tas2770->dev, "wrong power level setting %d\n", level);
- return -EINVAL;
- }
+ ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK, val);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -114,9 +102,7 @@ static int tas2770_codec_resume(struct snd_soc_component *component)
gpiod_set_value_cansleep(tas2770->sdz_gpio, 1);
usleep_range(1000, 2000);
} else {
- ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
- TAS2770_PWR_CTRL_MASK,
- TAS2770_PWR_CTRL_ACTIVE);
+ ret = tas2770_update_pwr_ctrl(tas2770);
if (ret < 0)
return ret;
}
@@ -152,24 +138,19 @@ static int tas2770_dac_event(struct snd_soc_dapm_widget *w,
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);
+ tas2770->dac_powered = 1;
+ ret = tas2770_update_pwr_ctrl(tas2770);
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);
+ tas2770->dac_powered = 0;
+ ret = tas2770_update_pwr_ctrl(tas2770);
break;
default:
dev_err(tas2770->dev, "Not supported evevt\n");
return -EINVAL;
}
- if (ret < 0)
- return ret;
-
- return 0;
+ return ret;
}
static const struct snd_kcontrol_new isense_switch =
@@ -203,21 +184,11 @@ static const struct snd_soc_dapm_route tas2770_audio_map[] = {
static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction)
{
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;
+ struct tas2770_priv *tas2770 =
+ snd_soc_component_get_drvdata(component);
- return 0;
+ tas2770->unmuted = !mute;
+ return tas2770_update_pwr_ctrl(tas2770);
}
static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)
@@ -337,7 +308,7 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct snd_soc_component *component = dai->component;
struct tas2770_priv *tas2770 =
snd_soc_component_get_drvdata(component);
- u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+ u8 tdm_rx_start_slot = 0, invert_fpol = 0, fpol_preinv = 0, asi_cfg_1 = 0;
int ret;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
@@ -349,9 +320,15 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ invert_fpol = 1;
+ fallthrough;
case SND_SOC_DAIFMT_NB_NF:
asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING;
break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert_fpol = 1;
+ fallthrough;
case SND_SOC_DAIFMT_IB_NF:
asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING;
break;
@@ -369,15 +346,19 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
tdm_rx_start_slot = 1;
+ fpol_preinv = 0;
break;
case SND_SOC_DAIFMT_DSP_A:
tdm_rx_start_slot = 0;
+ fpol_preinv = 1;
break;
case SND_SOC_DAIFMT_DSP_B:
tdm_rx_start_slot = 1;
+ fpol_preinv = 1;
break;
case SND_SOC_DAIFMT_LEFT_J:
tdm_rx_start_slot = 0;
+ fpol_preinv = 1;
break;
default:
dev_err(tas2770->dev,
@@ -391,6 +372,14 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
if (ret < 0)
return ret;
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_FPOL_MASK,
+ (fpol_preinv ^ invert_fpol)
+ ? TAS2770_TDM_CFG_REG0_FPOL_RSING
+ : TAS2770_TDM_CFG_REG0_FPOL_FALING);
+ if (ret < 0)
+ return ret;
+
return 0;
}
@@ -489,7 +478,7 @@ static struct snd_soc_dai_driver tas2770_dai_driver[] = {
.id = 0,
.playback = {
.stream_name = "ASI1 Playback",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = TAS2770_RATES,
.formats = TAS2770_FORMATS,
@@ -537,7 +526,6 @@ 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,
diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h
index d156666bcc55..f75f40781ab1 100644
--- a/sound/soc/codecs/tas2770.h
+++ b/sound/soc/codecs/tas2770.h
@@ -41,6 +41,9 @@
#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
+#define TAS2770_TDM_CFG_REG0_FPOL_MASK BIT(0)
+#define TAS2770_TDM_CFG_REG0_FPOL_RSING 0
+#define TAS2770_TDM_CFG_REG0_FPOL_FALING 1
/* TDM Configuration Reg1 */
#define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B)
#define TAS2770_TDM_CFG_REG1_MASK GENMASK(5, 1)
@@ -135,6 +138,8 @@ struct tas2770_priv {
struct device *dev;
int v_sense_slot;
int i_sense_slot;
+ bool dac_powered;
+ bool unmuted;
};
#endif /* __TAS2770__ */
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 2844a9d2bc4a..91a22d927915 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -31,6 +31,7 @@ struct adcx140_priv {
struct device *dev;
bool micbias_vg;
+ bool phase_calib_on;
unsigned int dai_fmt;
unsigned int slot_width;
@@ -592,6 +593,52 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = {
{"MIC4M Input Mux", "Digital", "MIC4M"},
};
+#define ADCX140_PHASE_CALIB_SWITCH(xname) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = adcx140_phase_calib_info, \
+ .get = adcx140_phase_calib_get, \
+ .put = adcx140_phase_calib_put}
+
+static int adcx140_phase_calib_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int adcx140_phase_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_soc_component *codec =
+ snd_soc_kcontrol_component(kcontrol);
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec);
+
+ value->value.integer.value[0] = adcx140->phase_calib_on ? 1 : 0;
+
+
+ return 0;
+}
+
+static int adcx140_phase_calib_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec);
+
+ bool v = value->value.integer.value[0] ? true : false;
+
+ if (adcx140->phase_calib_on != v) {
+ adcx140->phase_calib_on = v;
+ return 1;
+ }
+ return 0;
+}
+
static const struct snd_kcontrol_new adcx140_snd_controls[] = {
SOC_SINGLE_TLV("Analog CH1 Mic Gain Volume", ADCX140_CH1_CFG1, 2, 42, 0,
adc_tlv),
@@ -628,6 +675,7 @@ static const struct snd_kcontrol_new adcx140_snd_controls[] = {
0, 0xff, 0, dig_vol_tlv),
SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2,
0, 0xff, 0, dig_vol_tlv),
+ ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"),
};
static int adcx140_reset(struct adcx140_priv *adcx140)
@@ -653,6 +701,8 @@ static int adcx140_reset(struct adcx140_priv *adcx140)
static void adcx140_pwr_ctrl(struct adcx140_priv *adcx140, bool power_state)
{
int pwr_ctrl = 0;
+ int ret = 0;
+ struct snd_soc_component *component = adcx140->component;
if (power_state)
pwr_ctrl = ADCX140_PWR_CFG_ADC_PDZ | ADCX140_PWR_CFG_PLL_PDZ;
@@ -660,6 +710,14 @@ static void adcx140_pwr_ctrl(struct adcx140_priv *adcx140, bool power_state)
if (adcx140->micbias_vg && power_state)
pwr_ctrl |= ADCX140_PWR_CFG_BIAS_PDZ;
+ if (pwr_ctrl) {
+ ret = regmap_write(adcx140->regmap, ADCX140_PHASE_CALIB,
+ adcx140->phase_calib_on ? 0x00 : 0x40);
+ if (ret)
+ dev_err(component->dev, "%s: register write error %d\n",
+ __func__, ret);
+ }
+
regmap_update_bits(adcx140->regmap, ADCX140_PWR_CFG,
ADCX140_PWR_CTRL_MSK, pwr_ctrl);
}
@@ -1095,6 +1153,7 @@ static int adcx140_i2c_probe(struct i2c_client *i2c)
if (!adcx140)
return -ENOMEM;
+ adcx140->phase_calib_on = false;
adcx140->dev = &i2c->dev;
adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev,
diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h
index d7d4e3a88b5c..fd80fac8b327 100644
--- a/sound/soc/codecs/tlv320adcx140.h
+++ b/sound/soc/codecs/tlv320adcx140.h
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-// TLV320ADCX104 Sound driver
+// TLV320ADCX140 Sound driver
// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
#ifndef _TLV320ADCX140_H
@@ -90,6 +90,7 @@
#define ADCX140_PWR_CFG 0x75
#define ADCX140_DEV_STS0 0x76
#define ADCX140_DEV_STS1 0x77
+#define ADCX140_PHASE_CALIB 0X7b
#define ADCX140_RESET BIT(0)
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 8bae4b475068..e5dfb3d752a3 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -271,7 +271,7 @@ static ssize_t keyclick_show(struct device *dev,
freq = (125 << ((val >> 8) & 0x7)) >> 1;
len = 2 * (1 + ((val >> 4) & 0xf));
- return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+ return sysfs_emit(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
}
/* Any write to the keyclick attribute will trigger the keyclick event */
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 4b74805cdd2e..ffe1828a4b7e 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -49,6 +49,8 @@ struct aic32x4_priv {
struct aic32x4_setup_data *setup;
struct device *dev;
enum aic32x4_type type;
+
+ unsigned int fmt;
};
static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w,
@@ -611,6 +613,7 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct snd_soc_component *component = codec_dai->component;
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u8 iface_reg_1 = 0;
u8 iface_reg_2 = 0;
u8 iface_reg_3 = 0;
@@ -653,6 +656,8 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
+ aic32x4->fmt = fmt;
+
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
AIC32X4_IFACE1_DATATYPE_MASK |
AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
@@ -757,6 +762,10 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component,
return -EINVAL;
}
+ /* PCM over I2S is always 2-channel */
+ if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ channels = 2;
+
madc = DIV_ROUND_UP((32 * adc_resource_class), aosr);
max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) *
dosr_increment;
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 2db3d8a60c7a..1a62bec94005 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -450,7 +450,7 @@ static int uda134x_soc_probe(struct snd_soc_component *component)
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct uda134x_platform_data *pd = uda134x->pd;
const struct snd_soc_dapm_widget *widgets;
- unsigned num_widgets;
+ unsigned int num_widgets;
int ret;
printk(KERN_INFO "UDA134X SoC Audio Codec\n");
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index beeeb35e8032..2c5aa4df1e66 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5013,16 +5013,22 @@ static const struct regmap_irq wcd9335_codec_irqs[] = {
},
};
+static const unsigned int wcd9335_config_regs[] = {
+ WCD9335_INTR_LEVEL0,
+};
+
static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = {
.name = "wcd9335_pin1_irq",
.status_base = WCD9335_INTR_PIN1_STATUS0,
.mask_base = WCD9335_INTR_PIN1_MASK0,
.ack_base = WCD9335_INTR_PIN1_CLEAR0,
- .type_base = WCD9335_INTR_LEVEL0,
- .num_type_reg = 4,
.num_regs = 4,
.irqs = wcd9335_codec_irqs,
.num_irqs = ARRAY_SIZE(wcd9335_codec_irqs),
+ .config_base = wcd9335_config_regs,
+ .num_config_bases = ARRAY_SIZE(wcd9335_config_regs),
+ .num_config_regs = 4,
+ .set_type_config = regmap_irq_set_type_config_simple,
};
static int wcd9335_parse_dt(struct wcd9335_codec *wcd)
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index 781ae569be29..aca06a4026f3 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -1298,7 +1298,6 @@ static struct regmap_irq_chip wcd938x_regmap_irq_chip = {
.num_regs = 3,
.status_base = WCD938X_DIGITAL_INTR_STATUS_0,
.mask_base = WCD938X_DIGITAL_INTR_MASK_0,
- .type_base = WCD938X_DIGITAL_INTR_LEVEL_0,
.ack_base = WCD938X_DIGITAL_INTR_CLEAR_0,
.use_ack = 1,
.runtime_pm = true,
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 7523bb944b21..81f89f6767a2 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -114,11 +114,8 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
if (flags & FSL_SAI_CSR_SEF)
dev_dbg(dev, "isr: Tx Frame sync error detected\n");
- if (flags & FSL_SAI_CSR_FEF) {
+ if (flags & FSL_SAI_CSR_FEF)
dev_dbg(dev, "isr: Transmit underrun detected\n");
- /* FIFO reset for safety */
- xcsr |= FSL_SAI_CSR_FR;
- }
if (flags & FSL_SAI_CSR_FWF)
dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
@@ -148,11 +145,8 @@ irq_rx:
if (flags & FSL_SAI_CSR_SEF)
dev_dbg(dev, "isr: Rx Frame sync error detected\n");
- if (flags & FSL_SAI_CSR_FEF) {
+ if (flags & FSL_SAI_CSR_FEF)
dev_dbg(dev, "isr: Receive overflow detected\n");
- /* FIFO reset for safety */
- xcsr |= FSL_SAI_CSR_FR;
- }
if (flags & FSL_SAI_CSR_FWF)
dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
@@ -533,14 +527,17 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
u32 slot_width = word_width;
int adir = tx ? RX : TX;
u32 pins, bclk;
+ u32 watermark;
int ret, i;
- if (sai->slots)
- slots = sai->slots;
-
if (sai->slot_width)
slot_width = sai->slot_width;
+ if (sai->slots)
+ slots = sai->slots;
+ else if (sai->bclk_ratio)
+ slots = sai->bclk_ratio / slot_width;
+
pins = DIV_ROUND_UP(channels, slots);
/*
@@ -625,7 +622,15 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
FSL_SAI_CR5_FBT_MASK, val_cr5);
}
- if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) <= 1)
+ /*
+ * Combine mode has limation:
+ * - Can't used for singel dataline/FIFO case except the FIFO0
+ * - Can't used for multi dataline/FIFO case except the enabled FIFOs
+ * are successive and start from FIFO0
+ *
+ * So for common usage, all multi fifo case disable the combine mode.
+ */
+ if (hweight8(dl_cfg[dl_cfg_idx].mask[tx]) <= 1 || sai->is_multi_fifo_dma)
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FCOMB_MASK, 0);
else
@@ -636,6 +641,26 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
dma_params->addr = sai->res->start + FSL_SAI_xDR0(tx) +
dl_cfg[dl_cfg_idx].start_off[tx] * 0x4;
+ if (sai->is_multi_fifo_dma) {
+ sai->audio_config[tx].words_per_fifo = min(slots, channels);
+ if (tx) {
+ sai->audio_config[tx].n_fifos_dst = pins;
+ sai->audio_config[tx].stride_fifos_dst = dl_cfg[dl_cfg_idx].next_off[tx];
+ } else {
+ sai->audio_config[tx].n_fifos_src = pins;
+ sai->audio_config[tx].stride_fifos_src = dl_cfg[dl_cfg_idx].next_off[tx];
+ }
+ dma_params->maxburst = sai->audio_config[tx].words_per_fifo * pins;
+ dma_params->peripheral_config = &sai->audio_config[tx];
+ dma_params->peripheral_size = sizeof(sai->audio_config[tx]);
+
+ watermark = tx ? (sai->soc_data->fifo_depth - dma_params->maxburst) :
+ (dma_params->maxburst - 1);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR1(tx, ofs),
+ FSL_SAI_CR1_RFW_MASK(sai->soc_data->fifo_depth),
+ watermark);
+ }
+
/* Find a proper tcre setting */
for (i = 0; i < sai->soc_data->pins; i++) {
trce_mask = (1 << (i + 1)) - 1;
@@ -1263,6 +1288,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
char tmp[8];
int irq, ret, i;
int index;
+ u32 dmas[4];
sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
if (!sai)
@@ -1306,7 +1332,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->mclk_clk[i] = devm_clk_get(dev, tmp);
if (IS_ERR(sai->mclk_clk[i])) {
dev_err(dev, "failed to get mclk%d clock: %ld\n",
- i + 1, PTR_ERR(sai->mclk_clk[i]));
+ i, PTR_ERR(sai->mclk_clk[i]));
sai->mclk_clk[i] = NULL;
}
}
@@ -1319,6 +1345,11 @@ static int fsl_sai_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
&sai->pll11k_clk);
+ /* Use Multi FIFO mode depending on the support from SDMA script */
+ ret = of_property_read_u32_array(np, "dmas", dmas, 4);
+ if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
+ sai->is_multi_fifo_dma = true;
+
/* read dataline mask for rx and tx*/
ret = fsl_sai_read_dlcfg(sai);
if (ret < 0) {
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 17956b5731dc..697f6690068c 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -6,6 +6,7 @@
#ifndef __FSL_SAI_H
#define __FSL_SAI_H
+#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
@@ -281,6 +282,7 @@ struct fsl_sai {
bool is_lsb_first;
bool is_dsp_mode;
bool is_pdm_mode;
+ bool is_multi_fifo_dma;
bool synchronous[2];
struct fsl_sai_dl_cfg *dl_cfg;
unsigned int dl_cfg_cnt;
@@ -300,6 +302,7 @@ struct fsl_sai {
struct pm_qos_request pm_qos_req;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_state;
+ struct sdma_peripheral_config audio_config[2];
};
#define TX 1
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index 2e117311e582..4d99f4858a14 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -19,6 +19,7 @@
struct imx_rpmsg {
struct snd_soc_dai_link dai;
struct snd_soc_card card;
+ unsigned long sysclk;
};
static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = {
@@ -28,6 +29,27 @@ static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Main MIC", NULL),
};
+static int imx_rpmsg_late_probe(struct snd_soc_card *card)
+{
+ struct imx_rpmsg *data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_pcm_runtime *rtd = list_first_entry(&card->rtd_list,
+ struct snd_soc_pcm_runtime, list);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct device *dev = card->dev;
+ int ret;
+
+ if (!data->sysclk)
+ return 0;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, data->sysclk, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
static int imx_rpmsg_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link_component *dlc;
@@ -72,12 +94,18 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
data->dai.codecs->dai_name = "snd-soc-dummy-dai";
data->dai.codecs->name = "snd-soc-dummy";
} else {
+ struct clk *clk;
+
data->dai.codecs->of_node = args.np;
ret = snd_soc_get_dai_name(&args, &data->dai.codecs->dai_name);
if (ret) {
dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
goto fail;
}
+
+ clk = devm_get_clk_from_child(&pdev->dev, args.np, NULL);
+ if (!IS_ERR(clk))
+ data->sysclk = clk_get_rate(clk);
}
data->dai.cpus->dai_name = dev_name(&rpmsg_pdev->dev);
@@ -103,6 +131,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
data->card.owner = THIS_MODULE;
data->card.dapm_widgets = imx_rpmsg_dapm_widgets;
data->card.num_dapm_widgets = ARRAY_SIZE(imx_rpmsg_dapm_widgets);
+ data->card.late_probe = imx_rpmsg_late_probe;
/*
* Inoder to use common api to get card name and audio routing.
* Use parent of_node for this device, revert it after finishing using
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 4a29e314fa95..1b201dd09259 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -15,6 +15,33 @@
#include <sound/pcm_params.h>
#include <sound/simple_card_utils.h>
+static void asoc_simple_fixup_sample_fmt(struct asoc_simple_data *data,
+ struct snd_pcm_hw_params *params)
+{
+ int i;
+ struct snd_mask *mask = hw_param_mask(params,
+ SNDRV_PCM_HW_PARAM_FORMAT);
+ struct {
+ char *fmt;
+ u32 val;
+ } of_sample_fmt_table[] = {
+ { "s8", SNDRV_PCM_FORMAT_S8},
+ { "s16_le", SNDRV_PCM_FORMAT_S16_LE},
+ { "s24_le", SNDRV_PCM_FORMAT_S24_LE},
+ { "s24_3le", SNDRV_PCM_FORMAT_S24_3LE},
+ { "s32_le", SNDRV_PCM_FORMAT_S32_LE},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {
+ if (!strcmp(data->convert_sample_format,
+ of_sample_fmt_table[i].fmt)) {
+ snd_mask_none(mask);
+ snd_mask_set(mask, of_sample_fmt_table[i].val);
+ break;
+ }
+ }
+}
+
void asoc_simple_convert_fixup(struct asoc_simple_data *data,
struct snd_pcm_hw_params *params)
{
@@ -30,6 +57,9 @@ void asoc_simple_convert_fixup(struct asoc_simple_data *data,
if (data->convert_channels)
channels->min =
channels->max = data->convert_channels;
+
+ if (data->convert_sample_format)
+ asoc_simple_fixup_sample_fmt(data, params);
}
EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
@@ -49,6 +79,10 @@ void asoc_simple_parse_convert(struct device_node *np,
/* channels transfer */
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
of_property_read_u32(np, prop, &data->convert_channels);
+
+ /* convert sample format */
+ snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format");
+ of_property_read_string(np, prop, &data->convert_sample_format);
}
EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index ded903f95b67..d2ca710ac3fa 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -23,7 +23,7 @@ config SND_SOC_INTEL_CATPT
depends on ACPI || COMPILE_TEST
depends on DMADEVICES && SND_DMA_SGBUF
select DW_DMAC_CORE
- select SND_SOC_ACPI_INTEL_MATCH
+ select SND_SOC_ACPI if ACPI
select WANT_DEV_COREDUMP
select SND_INTEL_DSP_CONFIG
help
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index 160b50f479fb..a0d29510d2bc 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -242,11 +242,11 @@ static ssize_t firmware_version_show(struct device *dev,
if (ctx->fw_version.type == 0 && ctx->fw_version.major == 0 &&
ctx->fw_version.minor == 0 && ctx->fw_version.build == 0)
- return sprintf(buf, "FW not yet loaded\n");
+ return sysfs_emit(buf, "FW not yet loaded\n");
else
- return sprintf(buf, "v%02x.%02x.%02x.%02x\n",
- ctx->fw_version.type, ctx->fw_version.major,
- ctx->fw_version.minor, ctx->fw_version.build);
+ return sysfs_emit(buf, "v%02x.%02x.%02x.%02x\n",
+ ctx->fw_version.type, ctx->fw_version.major,
+ ctx->fw_version.minor, ctx->fw_version.build);
}
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index f21b0cdd3206..8fe5917b1e26 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -636,8 +636,8 @@ static ssize_t topology_name_read(struct file *file, char __user *user_buf, size
char buf[64];
size_t len;
- len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
- mach->tplg_filename);
+ len = scnprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
+ mach->tplg_filename);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index eea1e26acfda..53458e748191 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-soc-sst-haswell-objs := hsw_rt5640.o
+snd-soc-hsw-rt5640-objs := hsw_rt5640.o
snd-soc-sst-bdw-rt5650-mach-objs := bdw-rt5650.o
snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o
-snd-soc-sst-broadwell-objs := bdw_rt286.o
+snd-soc-bdw-rt286-objs := bdw_rt286.o
snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
snd-soc-sst-sof-pcm512x-objs := sof_pcm512x.o
@@ -47,13 +47,13 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_NAU8825_MACH) += snd-soc-sof_nau8825.o
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-hsw-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_PCM512x_MACH) += snd-soc-sst-sof-pcm512x.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_WM8804_MACH) += snd-soc-sst-sof-wm8804.o
obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o
-obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-bdw-rt286.o
obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5650_MACH) += snd-soc-sst-bdw-rt5650-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
diff --git a/sound/soc/intel/boards/sof_cirrus_common.c b/sound/soc/intel/boards/sof_cirrus_common.c
index f4192df962d6..6e39eda77385 100644
--- a/sound/soc/intel/boards/sof_cirrus_common.c
+++ b/sound/soc/intel/boards/sof_cirrus_common.c
@@ -10,6 +10,9 @@
#include "../../codecs/cs35l41.h"
#include "sof_cirrus_common.h"
+#define CS35L41_HID "CSC3541"
+#define CS35L41_MAX_AMPS 4
+
/*
* Cirrus Logic CS35L41/CS35L53
*/
@@ -35,50 +38,12 @@ static const struct snd_soc_dapm_route cs35l41_dapm_routes[] = {
{"TR Spk", NULL, "TR SPK"},
};
-static struct snd_soc_dai_link_component cs35l41_components[] = {
- {
- .name = CS35L41_DEV0_NAME,
- .dai_name = CS35L41_CODEC_DAI,
- },
- {
- .name = CS35L41_DEV1_NAME,
- .dai_name = CS35L41_CODEC_DAI,
- },
- {
- .name = CS35L41_DEV2_NAME,
- .dai_name = CS35L41_CODEC_DAI,
- },
- {
- .name = CS35L41_DEV3_NAME,
- .dai_name = CS35L41_CODEC_DAI,
- },
-};
+static struct snd_soc_dai_link_component cs35l41_components[CS35L41_MAX_AMPS];
/*
* Mapping between ACPI instance id and speaker position.
- *
- * Four speakers:
- * 0: Tweeter left, 1: Woofer left
- * 2: Tweeter right, 3: Woofer right
*/
-static struct snd_soc_codec_conf cs35l41_codec_conf[] = {
- {
- .dlc = COMP_CODEC_CONF(CS35L41_DEV0_NAME),
- .name_prefix = "TL",
- },
- {
- .dlc = COMP_CODEC_CONF(CS35L41_DEV1_NAME),
- .name_prefix = "WL",
- },
- {
- .dlc = COMP_CODEC_CONF(CS35L41_DEV2_NAME),
- .name_prefix = "TR",
- },
- {
- .dlc = COMP_CODEC_CONF(CS35L41_DEV3_NAME),
- .name_prefix = "WR",
- },
-};
+static struct snd_soc_codec_conf cs35l41_codec_conf[CS35L41_MAX_AMPS];
static int cs35l41_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -117,10 +82,10 @@ static int cs35l41_init(struct snd_soc_pcm_runtime *rtd)
static const struct {
unsigned int rx[2];
} cs35l41_channel_map[] = {
- {.rx = {0, 1}}, /* TL */
{.rx = {0, 1}}, /* WL */
- {.rx = {1, 0}}, /* TR */
{.rx = {1, 0}}, /* WR */
+ {.rx = {0, 1}}, /* TL */
+ {.rx = {1, 0}}, /* TR */
};
static int cs35l41_hw_params(struct snd_pcm_substream *substream,
@@ -175,10 +140,51 @@ static const struct snd_soc_ops cs35l41_ops = {
.hw_params = cs35l41_hw_params,
};
+static const char * const cs35l41_name_prefixes[] = { "WL", "WR", "TL", "TR" };
+
+/*
+ * Expected UIDs are integers (stored as strings).
+ * UID Mapping is fixed:
+ * UID 0x0 -> WL
+ * UID 0x1 -> WR
+ * UID 0x2 -> TL
+ * UID 0x3 -> TR
+ * Note: If there are less than 4 Amps, UIDs still map to WL/WR/TL/TR. Dynamic code will only create
+ * dai links for UIDs which exist, and ignore non-existant ones. Only 2 or 4 amps are expected.
+ * Return number of codecs found.
+ */
+static int cs35l41_compute_codec_conf(void)
+{
+ const char * const uid_strings[] = { "0", "1", "2", "3" };
+ unsigned int uid, sz = 0;
+ struct acpi_device *adev;
+ struct device *physdev;
+
+ for (uid = 0; uid < CS35L41_MAX_AMPS; uid++) {
+ adev = acpi_dev_get_first_match_dev(CS35L41_HID, uid_strings[uid], -1);
+ if (!adev) {
+ pr_devel("Cannot find match for HID %s UID %u (%s)\n", CS35L41_HID, uid,
+ cs35l41_name_prefixes[uid]);
+ continue;
+ }
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ cs35l41_components[sz].name = dev_name(physdev);
+ cs35l41_components[sz].dai_name = CS35L41_CODEC_DAI;
+ cs35l41_codec_conf[sz].dlc.name = dev_name(physdev);
+ cs35l41_codec_conf[sz].name_prefix = cs35l41_name_prefixes[uid];
+ acpi_dev_put(adev);
+ sz++;
+ }
+
+ if (sz != 2 && sz != 4)
+ pr_warn("Invalid number of cs35l41 amps found: %d, expected 2 or 4\n", sz);
+ return sz;
+}
+
void cs35l41_set_dai_link(struct snd_soc_dai_link *link)
{
+ link->num_codecs = cs35l41_compute_codec_conf();
link->codecs = cs35l41_components;
- link->num_codecs = ARRAY_SIZE(cs35l41_components);
link->init = cs35l41_init;
link->ops = &cs35l41_ops;
}
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index c7f33c89588e..606cc3242a60 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -760,6 +760,9 @@ static int sof_es8336_remove(struct platform_device *pdev)
static const struct platform_device_id board_ids[] = {
{
+ .name = "sof-essx8336", /* default quirk == 0 */
+ },
+ {
.name = "adl_es83x6_c1_h02",
.driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
@@ -786,5 +789,4 @@ module_platform_driver(sof_es8336_driver);
MODULE_DESCRIPTION("ASoC Intel(R) SOF + ES8336 Machine driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:sof-essx8336");
MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c
index d48a71d2cf1e..d5d08bd766c7 100644
--- a/sound/soc/intel/catpt/device.c
+++ b/sound/soc/intel/catpt/device.c
@@ -22,7 +22,6 @@
#include <sound/intel-dsp-config.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
-#include <sound/soc-acpi-intel-match.h>
#include "core.h"
#include "registers.h"
@@ -310,8 +309,36 @@ static int catpt_acpi_remove(struct platform_device *pdev)
return 0;
}
+static struct snd_soc_acpi_mach lpt_machines[] = {
+ {
+ .id = "INT33CA",
+ .drv_name = "hsw_rt5640",
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach wpt_machines[] = {
+ {
+ .id = "INT33CA",
+ .drv_name = "hsw_rt5640",
+ },
+ {
+ .id = "INT343A",
+ .drv_name = "bdw_rt286",
+ },
+ {
+ .id = "10EC5650",
+ .drv_name = "bdw-rt5650",
+ },
+ {
+ .id = "RT5677CE",
+ .drv_name = "bdw-rt5677",
+ },
+ {}
+};
+
static struct catpt_spec lpt_desc = {
- .machines = snd_soc_acpi_intel_haswell_machines,
+ .machines = lpt_machines,
.core_id = 0x01,
.host_dram_offset = 0x000000,
.host_iram_offset = 0x080000,
@@ -326,7 +353,7 @@ static struct catpt_spec lpt_desc = {
};
static struct catpt_spec wpt_desc = {
- .machines = snd_soc_acpi_intel_broadwell_machines,
+ .machines = wpt_machines,
.core_id = 0x02,
.host_dram_offset = 0x000000,
.host_iram_offset = 0x0A0000,
diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c
index 1bdbcc04dc71..9b6d2d93a2e7 100644
--- a/sound/soc/intel/catpt/sysfs.c
+++ b/sound/soc/intel/catpt/sysfs.c
@@ -27,8 +27,8 @@ static ssize_t fw_version_show(struct device *dev,
if (ret)
return CATPT_IPC_ERROR(ret);
- return sprintf(buf, "%d.%d.%d.%d\n", version.type, version.major,
- version.minor, version.build);
+ return sysfs_emit(buf, "%d.%d.%d.%d\n", version.type, version.major,
+ version.minor, version.build);
}
static DEVICE_ATTR_RO(fw_version);
@@ -37,7 +37,7 @@ static ssize_t fw_info_show(struct device *dev,
{
struct catpt_dev *cdev = dev_get_drvdata(dev);
- return sprintf(buf, "%s\n", cdev->ipc.config.fw_info);
+ return sysfs_emit(buf, "%s\n", cdev->ipc.config.fw_info);
}
static DEVICE_ATTR_RO(fw_info);
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index 8ca8f872ec80..41054cf09ec9 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -9,7 +9,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m
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-adl-match.o \
- soc-acpi-intel-mtl-match.o \
+ soc-acpi-intel-rpl-match.o soc-acpi-intel-mtl-match.o \
soc-acpi-intel-hda-match.o \
soc-acpi-intel-sdw-mockup-match.o
diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
index cbcb649604e5..6daf60b1edf1 100644
--- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
@@ -9,40 +9,25 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
-struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = {
- {
- .id = "INT33CA",
- .drv_name = "hsw_rt5640",
- .fw_filename = "intel/IntcSST1.bin",
- .sof_tplg_filename = "sof-hsw.tplg",
- },
- {}
-};
-EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_haswell_machines);
-
struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = {
{
.id = "INT343A",
.drv_name = "bdw_rt286",
- .fw_filename = "intel/IntcSST2.bin",
.sof_tplg_filename = "sof-bdw-rt286.tplg",
},
{
.id = "10EC5650",
.drv_name = "bdw-rt5650",
- .fw_filename = "intel/IntcSST2.bin",
.sof_tplg_filename = "sof-bdw-rt5650.tplg",
},
{
.id = "RT5677CE",
.drv_name = "bdw-rt5677",
- .fw_filename = "intel/IntcSST2.bin",
.sof_tplg_filename = "sof-bdw-rt5677.tplg",
},
{
.id = "INT33CA",
.drv_name = "hsw_rt5640",
- .fw_filename = "intel/IntcSST2.bin",
.sof_tplg_filename = "sof-bdw-rt5640.tplg",
},
{}
diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
new file mode 100644
index 000000000000..0b77401e4e6f
--- /dev/null
+++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * soc-apci-intel-rpl-match.c - tables and support for RPL ACPI enumeration.
+ *
+ * Copyright (c) 2022 Intel Corporation.
+ */
+
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+};
+
+static const struct snd_soc_acpi_adr_device rt711_0_adr[] = {
+ {
+ .adr = 0x000020025D071100ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr rpl_rvp[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_0_adr),
+ .adr_d = rt711_0_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines);
+
+/* this table is used when there is no I2S codec present */
+struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = {
+ {
+ .link_mask = 0x1, /* link0 required */
+ .links = rpl_rvp,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-rpl-rt711.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_sdw_machines);
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index deb7b820325e..e617b4c335a4 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -61,7 +61,7 @@ static ssize_t platform_id_show(struct device *dev,
nhlt->header.oem_revision);
skl_nhlt_trim_space(platform_id);
- return sprintf(buf, "%s\n", platform_id);
+ return sysfs_emit(buf, "%s\n", platform_id);
}
static DEVICE_ATTR_RO(platform_id);
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
index eb729ab00f5a..d7e94e6a19c7 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -1359,6 +1359,9 @@ static const struct snd_soc_dapm_widget mt8186_memif_widgets[] = {
SND_SOC_DAPM_MUX("UL5_IN_MUX", SND_SOC_NOPM, 0, 0,
&ul5_in_mux_control),
+ SND_SOC_DAPM_MIXER("DSP_DL1_VIRT", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DSP_DL2_VIRT", SND_SOC_NOPM, 0, 0, NULL, 0),
+
SND_SOC_DAPM_INPUT("UL1_VIRTUAL_INPUT"),
SND_SOC_DAPM_INPUT("UL2_VIRTUAL_INPUT"),
SND_SOC_DAPM_INPUT("UL3_VIRTUAL_INPUT"),
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c b/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c
index ec79e2f2a54d..e553a555d168 100644
--- a/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-i2s.c
@@ -658,9 +658,15 @@ static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
{"I2S1_CH1", "DL1_CH1 Switch", "DL1"},
{"I2S1_CH2", "DL1_CH2 Switch", "DL1"},
+ {"I2S1_CH1", "DL1_CH1 Switch", "DSP_DL1_VIRT"},
+ {"I2S1_CH2", "DL1_CH2 Switch", "DSP_DL1_VIRT"},
+
{"I2S1_CH1", "DL2_CH1 Switch", "DL2"},
{"I2S1_CH2", "DL2_CH2 Switch", "DL2"},
+ {"I2S1_CH1", "DL2_CH1 Switch", "DSP_DL2_VIRT"},
+ {"I2S1_CH2", "DL2_CH2 Switch", "DSP_DL2_VIRT"},
+
{"I2S1_CH1", "DL3_CH1 Switch", "DL3"},
{"I2S1_CH2", "DL3_CH2 Switch", "DL3"},
@@ -728,9 +734,15 @@ static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
{"I2S3_CH1", "DL1_CH1 Switch", "DL1"},
{"I2S3_CH2", "DL1_CH2 Switch", "DL1"},
+ {"I2S3_CH1", "DL1_CH1 Switch", "DSP_DL1_VIRT"},
+ {"I2S3_CH2", "DL1_CH2 Switch", "DSP_DL1_VIRT"},
+
{"I2S3_CH1", "DL2_CH1 Switch", "DL2"},
{"I2S3_CH2", "DL2_CH2 Switch", "DL2"},
+ {"I2S3_CH1", "DL2_CH1 Switch", "DSP_DL2_VIRT"},
+ {"I2S3_CH2", "DL2_CH2 Switch", "DSP_DL2_VIRT"},
+
{"I2S3_CH1", "DL3_CH1 Switch", "DL3"},
{"I2S3_CH2", "DL3_CH2 Switch", "DL3"},
@@ -968,7 +980,7 @@ static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
}
/* set share i2s */
- if (i2s_priv && i2s_priv->share_i2s_id >= 0) {
+ if (i2s_priv->share_i2s_id >= 0) {
ret = mtk_dai_i2s_config(afe, params, i2s_priv->share_i2s_id);
if (ret)
return ret;
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
index 387f25cad809..17a15bec41da 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c
@@ -18,6 +18,8 @@
#include "../../codecs/da7219.h"
#include "../../codecs/mt6358.h"
#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-dsp-sof-common.h"
+#include "../common/mtk-soc-card.h"
#include "mt8186-afe-common.h"
#include "mt8186-afe-clk.h"
#include "mt8186-afe-gpio.h"
@@ -26,6 +28,11 @@
#define DA7219_CODEC_DAI "da7219-hifi"
#define DA7219_DEV_NAME "da7219.5-001a"
+#define SOF_DMA_DL1 "SOF_DMA_DL1"
+#define SOF_DMA_DL2 "SOF_DMA_DL2"
+#define SOF_DMA_UL1 "SOF_DMA_UL1"
+#define SOF_DMA_UL2 "SOF_DMA_UL2"
+
struct mt8186_mt6366_da7219_max98357_priv {
struct snd_soc_jack headset_jack, hdmi_jack;
};
@@ -47,8 +54,9 @@ static struct snd_soc_codec_conf mt8186_mt6366_da7219_max98357_codec_conf[] = {
static int mt8186_da7219_init(struct snd_soc_pcm_runtime *rtd)
{
- struct mt8186_mt6366_da7219_max98357_priv *priv =
+ struct mtk_soc_card_data *soc_card_data =
snd_soc_card_get_drvdata(rtd->card);
+ struct mt8186_mt6366_da7219_max98357_priv *priv = soc_card_data->mach_priv;
struct snd_soc_jack *jack = &priv->headset_jack;
struct snd_soc_component *cmpnt_codec =
asoc_rtd_to_codec(rtd, 0)->component;
@@ -154,8 +162,9 @@ static int mt8186_mt6366_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *r
{
struct snd_soc_component *cmpnt_codec =
asoc_rtd_to_codec(rtd, 0)->component;
- struct mt8186_mt6366_da7219_max98357_priv *priv =
+ struct mtk_soc_card_data *soc_card_data =
snd_soc_card_get_drvdata(rtd->card);
+ struct mt8186_mt6366_da7219_max98357_priv *priv = soc_card_data->mach_priv;
int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, &priv->hdmi_jack);
@@ -201,6 +210,24 @@ static int mt8186_anx7625_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
return mt8186_hw_params_fixup(rtd, params, SNDRV_PCM_FORMAT_S24_LE);
}
+/* fixup the BE DAI link to match any values from topology */
+static int mt8186_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+
+ ret = mtk_sof_dai_link_fixup(rtd, params);
+
+ if (!strcmp(rtd->dai_link->name, "I2S0") ||
+ !strcmp(rtd->dai_link->name, "I2S1") ||
+ !strcmp(rtd->dai_link->name, "I2S2"))
+ mt8186_i2s_hw_params_fixup(rtd, params);
+ else if (!strcmp(rtd->dai_link->name, "I2S3"))
+ mt8186_anx7625_i2s_hw_params_fixup(rtd, params);
+
+ return ret;
+}
+
static int mt8186_mt6366_da7219_max98357_playback_startup(struct snd_pcm_substream *substream)
{
static const unsigned int rates[] = {
@@ -474,6 +501,33 @@ SND_SOC_DAILINK_DEFS(hostless_src_aaudio,
DAILINK_COMP_ARRAY(COMP_CPU("Hostless SRC AAudio DAI")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ { "I2S1", "AFE_SOF_DL1", SOF_DMA_DL1, SNDRV_PCM_STREAM_PLAYBACK},
+ { "I2S3", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK},
+ { "Primary Codec", "AFE_SOF_UL1", SOF_DMA_UL1, SNDRV_PCM_STREAM_CAPTURE},
+ { "I2S0", "AFE_SOF_UL2", SOF_DMA_UL2, SNDRV_PCM_STREAM_CAPTURE},
+};
+
static struct snd_soc_dai_link mt8186_mt6366_da7219_max98357_dai_links[] = {
/* Front End DAI links */
{
@@ -848,12 +902,41 @@ static struct snd_soc_dai_link mt8186_mt6366_da7219_max98357_dai_links[] = {
.ignore_suspend = 1,
SND_SOC_DAILINK_REG(hostless_ul6),
},
+ /* SOF BE */
+ {
+ .name = "AFE_SOF_DL1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL1),
+ },
+ {
+ .name = "AFE_SOF_DL2",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL2),
+ },
+ {
+ .name = "AFE_SOF_UL1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL1),
+ },
+ {
+ .name = "AFE_SOF_UL2",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL2),
+ },
};
static const struct snd_soc_dapm_widget
mt8186_mt6366_da7219_max98357_widgets[] = {
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_OUTPUT("HDMI1"),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL2, SND_SOC_NOPM, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route
@@ -862,6 +945,14 @@ mt8186_mt6366_da7219_max98357_routes[] = {
{ "Speakers", NULL, "Speaker"},
/* HDMI */
{ "HDMI1", NULL, "TX"},
+ /* SOF Uplink */
+ {SOF_DMA_UL1, NULL, "UL1_CH1"},
+ {SOF_DMA_UL1, NULL, "UL1_CH2"},
+ {SOF_DMA_UL2, NULL, "UL2_CH1"},
+ {SOF_DMA_UL2, NULL, "UL2_CH2"},
+ /* SOF Downlink */
+ {"DSP_DL1_VIRT", NULL, SOF_DMA_DL1},
+ {"DSP_DL2_VIRT", NULL, SOF_DMA_DL2},
};
static const struct snd_kcontrol_new
@@ -871,7 +962,7 @@ mt8186_mt6366_da7219_max98357_controls[] = {
};
static struct snd_soc_card mt8186_mt6366_da7219_max98357_soc_card = {
- .name = "mt8186_mt6366_da7219_max98357",
+ .name = "mt8186_da7219_max98357",
.owner = THIS_MODULE,
.dai_link = mt8186_mt6366_da7219_max98357_dai_links,
.num_links = ARRAY_SIZE(mt8186_mt6366_da7219_max98357_dai_links),
@@ -889,8 +980,10 @@ static int mt8186_mt6366_da7219_max98357_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
struct snd_soc_dai_link *dai_link;
- struct mt8186_mt6366_da7219_max98357_priv *priv;
- struct device_node *platform_node, *headset_codec, *playback_codec;
+ struct mtk_soc_card_data *soc_card_data;
+ struct mt8186_mt6366_da7219_max98357_priv *mach_priv;
+ struct device_node *platform_node, *headset_codec, *playback_codec, *adsp_node;
+ int sof_on = 0;
int ret, i;
card = (struct snd_soc_card *)device_get_match_data(&pdev->dev);
@@ -898,11 +991,60 @@ static int mt8186_mt6366_da7219_max98357_dev_probe(struct platform_device *pdev)
return -EINVAL;
card->dev = &pdev->dev;
+ soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*soc_card_data), GFP_KERNEL);
+ if (!soc_card_data)
+ return -ENOMEM;
+ mach_priv = devm_kzalloc(&pdev->dev, sizeof(*mach_priv), GFP_KERNEL);
+ if (!mach_priv)
+ return -ENOMEM;
+
+ soc_card_data->mach_priv = mach_priv;
+
+ adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0);
+ if (adsp_node) {
+ struct mtk_sof_priv *sof_priv;
+
+ sof_priv = devm_kzalloc(&pdev->dev, sizeof(*sof_priv), GFP_KERNEL);
+ if (!sof_priv) {
+ ret = -ENOMEM;
+ goto err_adsp_node;
+ }
+ sof_priv->conn_streams = g_sof_conn_streams;
+ sof_priv->num_streams = ARRAY_SIZE(g_sof_conn_streams);
+ sof_priv->sof_dai_link_fixup = mt8186_sof_dai_link_fixup;
+ soc_card_data->sof_priv = sof_priv;
+ card->probe = mtk_sof_card_probe;
+ card->late_probe = mtk_sof_card_late_probe;
+ if (!card->topology_shortname_created) {
+ snprintf(card->topology_shortname, 32, "sof-%s", card->name);
+ card->topology_shortname_created = true;
+ }
+ card->name = card->topology_shortname;
+ sof_on = 1;
+ } else {
+ dev_info(&pdev->dev, "Probe without adsp\n");
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
+ ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node,
+ "mediatek,dai-link",
+ mt8186_mt6366_da7219_max98357_dai_links,
+ ARRAY_SIZE(mt8186_mt6366_da7219_max98357_dai_links));
+ if (ret) {
+ dev_dbg(&pdev->dev, "Parse dai-link fail\n");
+ goto err_adsp_node;
+ }
+ } else {
+ if (!sof_on)
+ card->num_links = ARRAY_SIZE(mt8186_mt6366_da7219_max98357_dai_links)
+ - ARRAY_SIZE(g_sof_conn_streams);
+ }
+
platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
if (!platform_node) {
ret = -EINVAL;
dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n");
- return ret;
+ goto err_platform_node;
}
playback_codec = of_get_child_by_name(pdev->dev.of_node, "playback-codecs");
@@ -941,17 +1083,14 @@ static int mt8186_mt6366_da7219_max98357_dev_probe(struct platform_device *pdev)
goto err_probe;
}
- if (!dai_link->platforms->name)
- dai_link->platforms->of_node = platform_node;
- }
+ if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on)
+ dai_link->platforms->of_node = adsp_node;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- ret = -ENOMEM;
- goto err_probe;
+ if (!dai_link->platforms->name && !dai_link->platforms->of_node)
+ dai_link->platforms->of_node = platform_node;
}
- snd_soc_card_set_drvdata(card, priv);
+ snd_soc_card_set_drvdata(card, soc_card_data);
ret = mt8186_afe_gpio_init(&pdev->dev);
if (ret) {
@@ -969,6 +1108,9 @@ err_headset_codec:
of_node_put(playback_codec);
err_playback_codec:
of_node_put(platform_node);
+err_platform_node:
+err_adsp_node:
+ of_node_put(adsp_node);
return ret;
}
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
index 891146fd6c2b..393d179d61de 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
@@ -19,6 +19,8 @@
#include "../../codecs/mt6358.h"
#include "../../codecs/rt5682.h"
#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-dsp-sof-common.h"
+#include "../common/mtk-soc-card.h"
#include "mt8186-afe-common.h"
#include "mt8186-afe-clk.h"
#include "mt8186-afe-gpio.h"
@@ -30,6 +32,11 @@
#define RT5682S_CODEC_DAI "rt5682s-aif1"
#define RT5682S_DEV0_NAME "rt5682s.5-001a"
+#define SOF_DMA_DL1 "SOF_DMA_DL1"
+#define SOF_DMA_DL2 "SOF_DMA_DL2"
+#define SOF_DMA_UL1 "SOF_DMA_UL1"
+#define SOF_DMA_UL2 "SOF_DMA_UL2"
+
struct mt8186_mt6366_rt1019_rt5682s_priv {
struct snd_soc_jack headset_jack, hdmi_jack;
};
@@ -51,8 +58,9 @@ static struct snd_soc_codec_conf mt8186_mt6366_rt1019_rt5682s_codec_conf[] = {
static int mt8186_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
{
- struct mt8186_mt6366_rt1019_rt5682s_priv *priv =
+ struct mtk_soc_card_data *soc_card_data =
snd_soc_card_get_drvdata(rtd->card);
+ struct mt8186_mt6366_rt1019_rt5682s_priv *priv = soc_card_data->mach_priv;
struct snd_soc_jack *jack = &priv->headset_jack;
struct snd_soc_component *cmpnt_codec =
asoc_rtd_to_codec(rtd, 0)->component;
@@ -130,8 +138,9 @@ static int mt8186_mt6366_rt1019_rt5682s_hdmi_init(struct snd_soc_pcm_runtime *rt
{
struct snd_soc_component *cmpnt_codec =
asoc_rtd_to_codec(rtd, 0)->component;
- struct mt8186_mt6366_rt1019_rt5682s_priv *priv =
+ struct mtk_soc_card_data *soc_card_data =
snd_soc_card_get_drvdata(rtd->card);
+ struct mt8186_mt6366_rt1019_rt5682s_priv *priv = soc_card_data->mach_priv;
int ret;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, &priv->hdmi_jack);
@@ -177,6 +186,24 @@ static int mt8186_it6505_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
return mt8186_hw_params_fixup(rtd, params, SNDRV_PCM_FORMAT_S32_LE);
}
+/* fixup the BE DAI link to match any values from topology */
+static int mt8186_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+
+ ret = mtk_sof_dai_link_fixup(rtd, params);
+
+ if (!strcmp(rtd->dai_link->name, "I2S0") ||
+ !strcmp(rtd->dai_link->name, "I2S1") ||
+ !strcmp(rtd->dai_link->name, "I2S2"))
+ mt8186_i2s_hw_params_fixup(rtd, params);
+ else if (!strcmp(rtd->dai_link->name, "I2S3"))
+ mt8186_it6505_i2s_hw_params_fixup(rtd, params);
+
+ return ret;
+}
+
static int mt8186_mt6366_rt1019_rt5682s_playback_startup(struct snd_pcm_substream *substream)
{
static const unsigned int rates[] = {
@@ -450,6 +477,33 @@ SND_SOC_DAILINK_DEFS(hostless_src_aaudio,
DAILINK_COMP_ARRAY(COMP_CPU("Hostless SRC AAudio DAI")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ { "I2S1", "AFE_SOF_DL1", SOF_DMA_DL1, SNDRV_PCM_STREAM_PLAYBACK},
+ { "I2S3", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK},
+ { "Primary Codec", "AFE_SOF_UL1", SOF_DMA_UL1, SNDRV_PCM_STREAM_CAPTURE},
+ { "I2S0", "AFE_SOF_UL2", SOF_DMA_UL2, SNDRV_PCM_STREAM_CAPTURE},
+};
+
static struct snd_soc_dai_link mt8186_mt6366_rt1019_rt5682s_dai_links[] = {
/* Front End DAI links */
{
@@ -824,12 +878,41 @@ static struct snd_soc_dai_link mt8186_mt6366_rt1019_rt5682s_dai_links[] = {
.ignore_suspend = 1,
SND_SOC_DAILINK_REG(hostless_ul6),
},
+ /* SOF BE */
+ {
+ .name = "AFE_SOF_DL1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL1),
+ },
+ {
+ .name = "AFE_SOF_DL2",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL2),
+ },
+ {
+ .name = "AFE_SOF_UL1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL1),
+ },
+ {
+ .name = "AFE_SOF_UL2",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL2),
+ },
};
static const struct snd_soc_dapm_widget
mt8186_mt6366_rt1019_rt5682s_widgets[] = {
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_OUTPUT("HDMI1"),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL1, SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER(SOF_DMA_UL2, SND_SOC_NOPM, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route
@@ -838,6 +921,14 @@ mt8186_mt6366_rt1019_rt5682s_routes[] = {
{ "Speakers", NULL, "Speaker" },
/* HDMI */
{ "HDMI1", NULL, "TX" },
+ /* SOF Uplink */
+ {SOF_DMA_UL1, NULL, "UL1_CH1"},
+ {SOF_DMA_UL1, NULL, "UL1_CH2"},
+ {SOF_DMA_UL2, NULL, "UL2_CH1"},
+ {SOF_DMA_UL2, NULL, "UL2_CH2"},
+ /* SOF Downlink */
+ {"DSP_DL1_VIRT", NULL, SOF_DMA_DL1},
+ {"DSP_DL2_VIRT", NULL, SOF_DMA_DL2},
};
static const struct snd_kcontrol_new
@@ -847,7 +938,7 @@ mt8186_mt6366_rt1019_rt5682s_controls[] = {
};
static struct snd_soc_card mt8186_mt6366_rt1019_rt5682s_soc_card = {
- .name = "mt8186_mt6366_rt1019_rt5682s",
+ .name = "mt8186_rt1019_rt5682s",
.owner = THIS_MODULE,
.dai_link = mt8186_mt6366_rt1019_rt5682s_dai_links,
.num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links),
@@ -865,8 +956,10 @@ static int mt8186_mt6366_rt1019_rt5682s_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
struct snd_soc_dai_link *dai_link;
- struct mt8186_mt6366_rt1019_rt5682s_priv *priv;
- struct device_node *platform_node, *headset_codec, *playback_codec;
+ struct mtk_soc_card_data *soc_card_data;
+ struct mt8186_mt6366_rt1019_rt5682s_priv *mach_priv;
+ struct device_node *platform_node, *headset_codec, *playback_codec, *adsp_node;
+ int sof_on = 0;
int ret, i;
card = (struct snd_soc_card *)device_get_match_data(&pdev->dev);
@@ -874,11 +967,60 @@ static int mt8186_mt6366_rt1019_rt5682s_dev_probe(struct platform_device *pdev)
return -EINVAL;
card->dev = &pdev->dev;
+ soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*soc_card_data), GFP_KERNEL);
+ if (!soc_card_data)
+ return -ENOMEM;
+ mach_priv = devm_kzalloc(&pdev->dev, sizeof(*mach_priv), GFP_KERNEL);
+ if (!mach_priv)
+ return -ENOMEM;
+
+ soc_card_data->mach_priv = mach_priv;
+
+ adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0);
+ if (adsp_node) {
+ struct mtk_sof_priv *sof_priv;
+
+ sof_priv = devm_kzalloc(&pdev->dev, sizeof(*sof_priv), GFP_KERNEL);
+ if (!sof_priv) {
+ ret = -ENOMEM;
+ goto err_adsp_node;
+ }
+ sof_priv->conn_streams = g_sof_conn_streams;
+ sof_priv->num_streams = ARRAY_SIZE(g_sof_conn_streams);
+ sof_priv->sof_dai_link_fixup = mt8186_sof_dai_link_fixup;
+ soc_card_data->sof_priv = sof_priv;
+ card->probe = mtk_sof_card_probe;
+ card->late_probe = mtk_sof_card_late_probe;
+ if (!card->topology_shortname_created) {
+ snprintf(card->topology_shortname, 32, "sof-%s", card->name);
+ card->topology_shortname_created = true;
+ }
+ card->name = card->topology_shortname;
+ sof_on = 1;
+ } else {
+ dev_info(&pdev->dev, "Probe without adsp\n");
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {
+ ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node,
+ "mediatek,dai-link",
+ mt8186_mt6366_rt1019_rt5682s_dai_links,
+ ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links));
+ if (ret) {
+ dev_dbg(&pdev->dev, "Parse dai-link fail\n");
+ goto err_adsp_node;
+ }
+ } else {
+ if (!sof_on)
+ card->num_links = ARRAY_SIZE(mt8186_mt6366_rt1019_rt5682s_dai_links)
+ - ARRAY_SIZE(g_sof_conn_streams);
+ }
+
platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
if (!platform_node) {
ret = -EINVAL;
dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n");
- return ret;
+ goto err_platform_node;
}
playback_codec = of_get_child_by_name(pdev->dev.of_node, "playback-codecs");
@@ -917,17 +1059,14 @@ static int mt8186_mt6366_rt1019_rt5682s_dev_probe(struct platform_device *pdev)
goto err_probe;
}
- if (!dai_link->platforms->name)
- dai_link->platforms->of_node = platform_node;
- }
+ if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on)
+ dai_link->platforms->of_node = adsp_node;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- ret = -ENOMEM;
- goto err_probe;
+ if (!dai_link->platforms->name && !dai_link->platforms->of_node)
+ dai_link->platforms->of_node = platform_node;
}
- snd_soc_card_set_drvdata(card, priv);
+ snd_soc_card_set_drvdata(card, soc_card_data);
ret = mt8186_afe_gpio_init(&pdev->dev);
if (ret) {
@@ -945,6 +1084,9 @@ err_headset_codec:
of_node_put(playback_codec);
err_playback_codec:
of_node_put(platform_node);
+err_platform_node:
+err_adsp_node:
+ of_node_put(adsp_node);
return ret;
}
diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c
index a26cda5140c1..73b0cbac73d4 100644
--- a/sound/soc/qcom/qdsp6/q6prm-clocks.c
+++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c
@@ -50,6 +50,15 @@ static const struct q6dsp_clk_init q6prm_clks[] = {
Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK),
Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK),
Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_TX_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_TX_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_TX_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_TX_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK),
+ Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK),
Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS,
"LPASS_HW_MACRO"),
Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC,
diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h
index fea4d1954bc1..a988a32086fe 100644
--- a/sound/soc/qcom/qdsp6/q6prm.h
+++ b/sound/soc/qcom/qdsp6/q6prm.h
@@ -64,6 +64,25 @@
#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK 0x30e
#define Q6PRM_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f
+/* Clock ID for MCLK for WSA2 core */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_MCLK 0x310
+/* Clock ID for NPL MCLK for WSA2 core */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_2X_MCLK 0x311
+/* Clock ID for RX Core TX MCLK */
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_TX_MCLK 0x312
+/* Clock ID for RX CORE TX 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_TX_2X_MCLK 0x313
+/* Clock ID for WSA core TX MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_TX_MCLK 0x314
+/* Clock ID for WSA core TX 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA_CORE_TX_2X_MCLK 0x315
+/* Clock ID for WSA2 core TX MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_TX_MCLK 0x316
+/* Clock ID for WSA2 core TX 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK 0x317
+/* Clock ID for RX CORE MCLK2 2X MCLK */
+#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318
+
#define Q6PRM_LPASS_CLK_SRC_INTERNAL 1
#define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0
#define Q6PRM_HW_CORE_ID_LPASS 1
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
index e7d52d27132e..0fbbf3b02c09 100644
--- a/sound/soc/samsung/aries_wm8994.c
+++ b/sound/soc/samsung/aries_wm8994.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
#include <linux/extcon.h>
#include <linux/iio/consumer.h>
-#include <linux/iio/iio.h>
#include <linux/input-event-codes.h>
#include <linux/mfd/wm8994/registers.h>
#include <linux/module.h>
@@ -543,6 +542,7 @@ static int aries_audio_probe(struct platform_device *pdev)
struct aries_wm8994_data *priv;
struct snd_soc_dai_link *dai_link;
const struct of_device_id *match;
+ enum iio_chan_type channel_type;
int ret, i;
if (!np)
@@ -594,7 +594,11 @@ static int aries_audio_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(priv->adc),
"Failed to get ADC channel");
- if (priv->adc->channel->type != IIO_VOLTAGE)
+ ret = iio_get_channel_type(priv->adc, &channel_type);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get ADC channel type");
+ if (channel_type != IIO_VOLTAGE)
return -EINVAL;
priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c
index 0d0594a0e4f6..7ace0c0db5b1 100644
--- a/sound/soc/sh/rz-ssi.c
+++ b/sound/soc/sh/rz-ssi.c
@@ -1017,32 +1017,36 @@ static int rz_ssi_probe(struct platform_device *pdev)
ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(ssi->rstc)) {
- rz_ssi_release_dma_channels(ssi);
- return PTR_ERR(ssi->rstc);
+ ret = PTR_ERR(ssi->rstc);
+ goto err_reset;
}
reset_control_deassert(ssi->rstc);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0) {
- rz_ssi_release_dma_channels(ssi);
- pm_runtime_disable(ssi->dev);
- reset_control_assert(ssi->rstc);
- return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n");
+ dev_err(&pdev->dev, "pm_runtime_resume_and_get failed\n");
+ goto err_pm;
}
ret = devm_snd_soc_register_component(&pdev->dev, &rz_ssi_soc_component,
rz_ssi_soc_dai,
ARRAY_SIZE(rz_ssi_soc_dai));
if (ret < 0) {
- rz_ssi_release_dma_channels(ssi);
-
- pm_runtime_put(ssi->dev);
- pm_runtime_disable(ssi->dev);
- reset_control_assert(ssi->rstc);
dev_err(&pdev->dev, "failed to register snd component\n");
+ goto err_snd_soc;
}
+ return 0;
+
+err_snd_soc:
+ pm_runtime_put(ssi->dev);
+err_pm:
+ pm_runtime_disable(ssi->dev);
+ reset_control_assert(ssi->rstc);
+err_reset:
+ rz_ssi_release_dma_channels(ssi);
+
return ret;
}
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index 5f49e3dec3fc..32c5be61e2ec 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -57,7 +57,7 @@ static inline struct snd_soc_component *gpio_to_component(struct gpio_chip *chip
return gpio_priv->component;
}
-static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
+static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
if (offset >= AC97_NUM_GPIOS)
return -EINVAL;
@@ -66,7 +66,7 @@ static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
}
static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
- unsigned offset)
+ unsigned int offset)
{
struct snd_soc_component *component = gpio_to_component(chip);
@@ -75,7 +75,7 @@ static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
1 << offset, 1 << offset);
}
-static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
+static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct snd_soc_component *component = gpio_to_component(chip);
int ret;
@@ -88,7 +88,7 @@ static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
return !!(ret & (1 << offset));
}
-static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
+static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct snd_ac97_gpio_priv *gpio_priv = gpiochip_get_data(chip);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e824ff1a9fc0..e020ab49cfb1 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -72,7 +72,7 @@ static ssize_t pmdown_time_show(struct device *dev,
{
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
- return sprintf(buf, "%ld\n", rtd->pmdown_time);
+ return sysfs_emit(buf, "%ld\n", rtd->pmdown_time);
}
static ssize_t pmdown_time_store(struct device *dev,
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index d530e8c2b77b..49752af0e205 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -124,7 +124,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
*/
int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- int ret = -EINVAL;
+ int ret = -ENOTSUPP;
if (dai->driver->ops &&
dai->driver->ops->set_bclk_ratio)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b05231414c1d..73b8bd452ca7 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2386,11 +2386,10 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
- char *buf)
+ char *buf, int count)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
struct snd_soc_dapm_widget *w;
- int count = 0;
char *state = "not set";
/* card won't be set for the dummy component, as a spot fix
@@ -2423,7 +2422,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
case snd_soc_dapm_pinctrl:
case snd_soc_dapm_clock_supply:
if (w->name)
- count += sprintf(buf + count, "%s: %s\n",
+ count += sysfs_emit_at(buf, count, "%s: %s\n",
w->name, w->power ? "On":"Off");
break;
default:
@@ -2445,7 +2444,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
state = "Off";
break;
}
- count += sprintf(buf + count, "PM State: %s\n", state);
+ count += sysfs_emit_at(buf, count, "PM State: %s\n", state);
return count;
}
@@ -2463,7 +2462,7 @@ static ssize_t dapm_widget_show(struct device *dev,
for_each_rtd_codec_dais(rtd, i, codec_dai) {
struct snd_soc_component *cmpnt = codec_dai->component;
- count += dapm_widget_show_component(cmpnt, buf + count);
+ count = dapm_widget_show_component(cmpnt, buf, count);
}
mutex_unlock(&rtd->card->dapm_mutex);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 5b99bf2dbd08..b5720e272c51 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -723,7 +723,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
snd_soc_dpcm_mutex_lock(rtd);
- soc_pcm_clean(rtd, substream, 0);
+ __soc_pcm_close(rtd, substream);
snd_soc_dpcm_mutex_unlock(rtd);
return 0;
}
@@ -1317,6 +1317,9 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (!be->dai_link->no_pcm)
continue;
+ if (!snd_soc_dpcm_get_substream(be, stream))
+ continue;
+
for_each_rtd_dais(be, i, dai) {
w = snd_soc_dai_get_widget(dai, stream);
@@ -2904,6 +2907,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->pcm = pcm;
pcm->nonatomic = rtd->dai_link->nonatomic;
pcm->private_data = rtd;
+ pcm->no_device_suspend = true;
if (rtd->dai_link->no_pcm || rtd->dai_link->params) {
if (playback)
@@ -2958,8 +2962,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
ret = snd_soc_pcm_component_new(rtd);
if (ret < 0)
return ret;
-
- pcm->no_device_suspend = true;
out:
dev_dbg(rtd->card->dev, "%s <-> %s mapping ok\n",
soc_codec_dai_name(rtd), soc_cpu_dai_name(rtd));
diff --git a/sound/soc/soc-utils-test.c b/sound/soc/soc-utils-test.c
index 5ad8e23af49a..616d2c926dd1 100644
--- a/sound/soc/soc-utils-test.c
+++ b/sound/soc/soc-utils-test.c
@@ -170,8 +170,54 @@ static void test_tdm_params_to_bclk(struct kunit *test)
}
}
+static void test_snd_soc_params_to_bclk_one(struct kunit *test,
+ unsigned int rate, snd_pcm_format_t fmt,
+ unsigned int channels,
+ unsigned int expected_bclk)
+{
+ struct snd_pcm_hw_params params;
+ int got_bclk;
+
+ _snd_pcm_hw_params_any(&params);
+ snd_mask_none(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT));
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->min = rate;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->max = rate;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = channels;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->max = channels;
+ params_set_format(&params, fmt);
+
+ got_bclk = snd_soc_params_to_bclk(&params);
+ pr_debug("%s: r=%u sb=%u ch=%u expected=%u got=%d\n",
+ __func__,
+ rate, params_width(&params), channels, expected_bclk, got_bclk);
+ KUNIT_ASSERT_EQ(test, expected_bclk, (unsigned int)got_bclk);
+}
+
+static void test_snd_soc_params_to_bclk(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tdm_params_to_bclk_cases); ++i) {
+ /*
+ * snd_soc_params_to_bclk() is all the test cases where
+ * snd_pcm_hw_params values are not overridden.
+ */
+ if (tdm_params_to_bclk_cases[i].tdm_width |
+ tdm_params_to_bclk_cases[i].tdm_slots |
+ tdm_params_to_bclk_cases[i].slot_multiple)
+ continue;
+
+ test_snd_soc_params_to_bclk_one(test,
+ tdm_params_to_bclk_cases[i].rate,
+ tdm_params_to_bclk_cases[i].fmt,
+ tdm_params_to_bclk_cases[i].channels,
+ tdm_params_to_bclk_cases[i].bclk);
+ }
+}
+
static struct kunit_case soc_utils_test_cases[] = {
KUNIT_CASE(test_tdm_params_to_bclk),
+ KUNIT_CASE(test_snd_soc_params_to_bclk),
{}
};
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 70c380c0ac7b..a3b6df2378b4 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -56,23 +56,24 @@ EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
/**
* snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info.
*
- * Calculate the bclk from the params sample rate and the tdm slot count and
- * tdm slot width. Either or both of tdm_width and tdm_slots can be 0.
+ * Calculate the bclk from the params sample rate, the tdm slot count and the
+ * tdm slot width. Optionally round-up the slot count to a given multiple.
+ * Either or both of tdm_width and tdm_slots can be 0.
*
- * If tdm_width == 0 and tdm_slots > 0: the params_width will be used.
- * If tdm_width > 0 and tdm_slots == 0: the params_channels will be used
- * as the slot count.
- * Both tdm_width and tdm_slots are 0: this is equivalent to calling
- * snd_soc_params_to_bclk().
+ * If tdm_width == 0: use params_width() as the slot width.
+ * If tdm_slots == 0: use params_channels() as the slot count.
*
- * If slot_multiple > 1 the slot count (or params_channels if tdm_slots == 0)
- * will be rounded up to a multiple of this value. This is mainly useful for
+ * If slot_multiple > 1 the slot count (or params_channels() if tdm_slots == 0)
+ * will be rounded up to a multiple of slot_multiple. This is mainly useful for
* I2S mode, which has a left and right phase so the number of slots is always
* a multiple of 2.
*
+ * If tdm_width == 0 && tdm_slots == 0 && slot_multiple < 2, this is equivalent
+ * to calling snd_soc_params_to_bclk().
+ *
* @params: Pointer to struct_pcm_hw_params.
- * @tdm_width: Width in bits of the tdm slots.
- * @tdm_slots: Number of tdm slots per frame.
+ * @tdm_width: Width in bits of the tdm slots. Must be >= 0.
+ * @tdm_slots: Number of tdm slots per frame. Must be >= 0.
* @slot_multiple: If >1 roundup slot count to a multiple of this value.
*
* Return: bclk frequency in Hz, else a negative error code if params format
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 67139e15f862..c8ae778a50d1 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -11,20 +11,20 @@
#include "sof-priv.h"
#include "sof-utils.h"
-static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp,
+static void sof_set_transferred_bytes(struct sof_compr_stream *sstream,
u64 host_pos, u64 buffer_size)
{
u64 prev_pos;
unsigned int copied;
- div64_u64_rem(tstamp->copied_total, buffer_size, &prev_pos);
+ div64_u64_rem(sstream->copied_total, buffer_size, &prev_pos);
if (host_pos < prev_pos)
copied = (buffer_size - prev_pos) + host_pos;
else
copied = host_pos - prev_pos;
- tstamp->copied_total += copied;
+ sstream->copied_total += copied;
}
static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)
@@ -49,7 +49,7 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
struct snd_soc_pcm_runtime *rtd;
struct snd_compr_runtime *crtd;
struct snd_soc_component *component;
- struct snd_compr_tstamp *tstamp;
+ struct sof_compr_stream *sstream;
struct snd_sof_pcm *spcm;
if (!cstream)
@@ -57,7 +57,7 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
rtd = cstream->private_data;
crtd = cstream->runtime;
- tstamp = crtd->private_data;
+ sstream = crtd->private_data;
component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -67,7 +67,7 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
return;
}
- sof_set_transferred_bytes(tstamp, spcm->stream[cstream->direction].posn.host_posn,
+ sof_set_transferred_bytes(sstream, spcm->stream[cstream->direction].posn.host_posn,
crtd->buffer_size);
/* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */
@@ -96,24 +96,24 @@ static int sof_compr_open(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *crtd = cstream->runtime;
- struct snd_compr_tstamp *tstamp;
+ struct sof_compr_stream *sstream;
struct snd_sof_pcm *spcm;
int dir;
- tstamp = kzalloc(sizeof(*tstamp), GFP_KERNEL);
- if (!tstamp)
+ sstream = kzalloc(sizeof(*sstream), GFP_KERNEL);
+ if (!sstream)
return -ENOMEM;
spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm) {
- kfree(tstamp);
+ kfree(sstream);
return -EINVAL;
}
dir = cstream->direction;
if (spcm->stream[dir].cstream) {
- kfree(tstamp);
+ kfree(sstream);
return -EBUSY;
}
@@ -122,7 +122,7 @@ static int sof_compr_open(struct snd_soc_component *component,
spcm->stream[dir].posn.dai_posn = 0;
spcm->prepared[dir] = false;
- crtd->private_data = tstamp;
+ crtd->private_data = sstream;
return 0;
}
@@ -131,7 +131,7 @@ static int sof_compr_free(struct snd_soc_component *component,
struct snd_compr_stream *cstream)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct snd_compr_tstamp *tstamp = cstream->runtime->private_data;
+ struct sof_compr_stream *sstream = cstream->runtime->private_data;
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
@@ -155,7 +155,7 @@ static int sof_compr_free(struct snd_soc_component *component,
cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work);
spcm->stream[cstream->direction].cstream = NULL;
- kfree(tstamp);
+ kfree(sstream);
return ret;
}
@@ -169,7 +169,7 @@ static int sof_compr_set_params(struct snd_soc_component *component,
struct sof_ipc_pcm_params_reply ipc_params_reply;
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
- struct snd_compr_tstamp *tstamp;
+ struct sof_compr_stream *sstream;
struct sof_ipc_pcm_params *pcm;
struct snd_sof_pcm *spcm;
size_t ext_data_size;
@@ -184,7 +184,7 @@ static int sof_compr_set_params(struct snd_soc_component *component,
return -EINVAL;
}
- tstamp = crtd->private_data;
+ sstream = crtd->private_data;
spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -237,8 +237,9 @@ static int sof_compr_set_params(struct snd_soc_component *component,
goto out;
}
- tstamp->byte_offset = sdev->stream_box.offset + ipc_params_reply.posn_offset;
- tstamp->sampling_rate = params->codec.sample_rate;
+ sstream->sampling_rate = params->codec.sample_rate;
+ sstream->channels = params->codec.ch_out;
+ sstream->sample_container_bytes = pcm->params.sample_container_bytes;
spcm->prepared[cstream->direction] = true;
@@ -326,10 +327,18 @@ static int sof_compr_pointer(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp)
{
- struct snd_compr_tstamp *pstamp = cstream->runtime->private_data;
+ struct snd_sof_pcm *spcm;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct sof_compr_stream *sstream = cstream->runtime->private_data;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
- tstamp->sampling_rate = pstamp->sampling_rate;
- tstamp->copied_total = pstamp->copied_total;
+ tstamp->sampling_rate = sstream->sampling_rate;
+ tstamp->copied_total = sstream->copied_total;
+ tstamp->pcm_io_frames = div_u64(spcm->stream[cstream->direction].posn.dai_posn,
+ sstream->channels * sstream->sample_container_bytes);
return 0;
}
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index c5d797e97c02..d9a3ce7b69e1 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -252,9 +252,9 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
}
for (i = 0, len = 0; i < reply->num_elems; i++) {
- ret = snprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n",
- reply->elems[i].zone, reply->elems[i].id,
- reply->elems[i].used, reply->elems[i].free);
+ ret = scnprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n",
+ reply->elems[i].zone, reply->elems[i].id,
+ reply->elems[i].used, reply->elems[i].free);
if (ret < 0)
goto error;
len += ret;
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig
index cc6e695f913a..4751b04d5e6f 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -41,4 +41,13 @@ config SND_SOC_SOF_IMX8M
Say Y if you have such a device.
If unsure select "N".
+config SND_SOC_SOF_IMX8ULP
+ tristate "SOF support for i.MX8ULP"
+ depends on IMX_DSP
+ select SND_SOC_SOF_IMX_COMMON
+ help
+ This adds support for Sound Open Firmware for NXP i.MX8ULP platforms.
+ Say Y if you have such a device.
+ If unsure select "N".
+
endif ## SND_SOC_SOF_IMX_TOPLEVEL
diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile
index dba93c3466ec..798b43a415bf 100644
--- a/sound/soc/sof/imx/Makefile
+++ b/sound/soc/sof/imx/Makefile
@@ -1,9 +1,11 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-imx8-objs := imx8.o
snd-sof-imx8m-objs := imx8m.o
+snd-sof-imx8ulp-objs := imx8ulp.o
snd-sof-imx-common-objs := imx-common.o
obj-$(CONFIG_SND_SOC_SOF_IMX8) += snd-sof-imx8.o
obj-$(CONFIG_SND_SOC_SOF_IMX8M) += snd-sof-imx8m.o
+obj-$(CONFIG_SND_SOC_SOF_IMX8ULP) += snd-sof-imx8ulp.o
obj-$(CONFIG_SND_SOC_SOF_IMX_COMMON) += imx-common.o
diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c
new file mode 100644
index 000000000000..4a562c9856e9
--- /dev/null
+++ b/sound/soc/sof/imx/imx8ulp.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright 2021-2022 NXP
+//
+// Author: Peng Zhang <peng.zhang_8@nxp.com>
+//
+// Hardware interface for audio DSP on i.MX8ULP
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/firmware/imx/dsp.h>
+#include <linux/firmware/imx/ipc.h>
+#include <linux/firmware/imx/svc/misc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+
+#include "../ops.h"
+#include "../sof-of-dev.h"
+#include "imx-common.h"
+
+#define FSL_SIP_HIFI_XRDC 0xc200000e
+
+/* SIM Domain register */
+#define SYSCTRL0 0x8
+#define EXECUTE_BIT BIT(13)
+#define RESET_BIT BIT(16)
+#define HIFI4_CLK_BIT BIT(17)
+#define PB_CLK_BIT BIT(18)
+#define PLAT_CLK_BIT BIT(19)
+#define DEBUG_LOGIC_BIT BIT(25)
+
+#define MBOX_OFFSET 0x800000
+#define MBOX_SIZE 0x1000
+
+static struct clk_bulk_data imx8ulp_dsp_clks[] = {
+ { .id = "core" },
+ { .id = "ipg" },
+ { .id = "ocram" },
+ { .id = "mu" },
+};
+
+struct imx8ulp_priv {
+ struct device *dev;
+ struct snd_sof_dev *sdev;
+
+ /* DSP IPC handler */
+ struct imx_dsp_ipc *dsp_ipc;
+ struct platform_device *ipc_dev;
+
+ struct regmap *regmap;
+ struct imx_clocks *clks;
+};
+
+static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv)
+{
+ /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
+ regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0);
+
+ /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/
+ regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
+
+ /* Stall HIFI4 DSP Execution: 1 stall, 0 run */
+ regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0);
+}
+
+static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MBOX_OFFSET;
+}
+
+static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MBOX_OFFSET;
+}
+
+static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc)
+{
+ struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
+
+ snd_sof_ipc_process_reply(priv->sdev, 0);
+
+ spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
+}
+
+static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc)
+{
+ struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
+ u32 p; /* panic code */
+
+ /* Read the message from the debug box. */
+ sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
+
+ /* Check to see if the message is a panic code (0x0dead***) */
+ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
+ snd_sof_dsp_panic(priv->sdev, p, true);
+ else
+ snd_sof_ipc_msgs_rx(priv->sdev);
+}
+
+static struct imx_dsp_ops dsp_ops = {
+ .handle_reply = imx8ulp_dsp_handle_reply,
+ .handle_request = imx8ulp_dsp_handle_request,
+};
+
+static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
+
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+ msg->msg_size);
+ imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
+
+ return 0;
+}
+
+static int imx8ulp_run(struct snd_sof_dev *sdev)
+{
+ struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
+
+ imx8ulp_sim_lpav_start(priv);
+
+ return 0;
+}
+
+static int imx8ulp_reset(struct snd_sof_dev *sdev)
+{
+ struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
+ struct arm_smccc_res smc_resource;
+
+ /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
+ regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
+
+ /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
+ regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
+
+ /* HiFi4 Clock Enable: 1 enabled, 0 disabled */
+ regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
+
+ regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
+ usleep_range(1, 2);
+
+ /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
+ regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
+ usleep_range(1, 2);
+
+ arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource);
+
+ return 0;
+}
+
+static int imx8ulp_probe(struct snd_sof_dev *sdev)
+{
+ struct platform_device *pdev =
+ container_of(sdev->dev, struct platform_device, dev);
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *res_node;
+ struct resource *mmio;
+ struct imx8ulp_priv *priv;
+ struct resource res;
+ u32 base, size;
+ int ret = 0;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL);
+ if (!priv->clks)
+ return -ENOMEM;
+
+ sdev->num_cores = 1;
+ sdev->pdata->hw_pdata = priv;
+ priv->dev = sdev->dev;
+ priv->sdev = sdev;
+
+ /* System integration module(SIM) control dsp configuration */
+ priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl");
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
+ PLATFORM_DEVID_NONE,
+ pdev, sizeof(*pdev));
+ if (IS_ERR(priv->ipc_dev))
+ return PTR_ERR(priv->ipc_dev);
+
+ priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
+ if (!priv->dsp_ipc) {
+ /* DSP IPC driver not probed yet, try later */
+ ret = -EPROBE_DEFER;
+ dev_err(sdev->dev, "Failed to get drvdata\n");
+ goto exit_pdev_unregister;
+ }
+
+ imx_dsp_set_data(priv->dsp_ipc, priv);
+ priv->dsp_ipc->ops = &dsp_ops;
+
+ /* DSP base */
+ mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mmio) {
+ base = mmio->start;
+ size = resource_size(mmio);
+ } else {
+ dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
+ ret = -EINVAL;
+ goto exit_pdev_unregister;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
+ dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
+ base, size);
+ ret = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+ sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
+
+ res_node = of_parse_phandle(np, "memory-reserved", 0);
+ if (!res_node) {
+ dev_err(&pdev->dev, "failed to get memory region node\n");
+ ret = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+
+ ret = of_address_to_resource(res_node, 0, &res);
+ of_node_put(res_node);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get reserved region address\n");
+ goto exit_pdev_unregister;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
+ resource_size(&res));
+ if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
+ dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
+ base, size);
+ ret = -ENOMEM;
+ goto exit_pdev_unregister;
+ }
+ sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = MBOX_OFFSET;
+
+ ret = of_reserved_mem_device_init(sdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret);
+ goto exit_pdev_unregister;
+ }
+
+ priv->clks->dsp_clks = imx8ulp_dsp_clks;
+ priv->clks->num_dsp_clks = ARRAY_SIZE(imx8ulp_dsp_clks);
+
+ ret = imx8_parse_clocks(sdev, priv->clks);
+ if (ret < 0)
+ goto exit_pdev_unregister;
+
+ ret = imx8_enable_clocks(sdev, priv->clks);
+ if (ret < 0)
+ goto exit_pdev_unregister;
+
+ return 0;
+
+exit_pdev_unregister:
+ platform_device_unregister(priv->ipc_dev);
+
+ return ret;
+}
+
+static int imx8ulp_remove(struct snd_sof_dev *sdev)
+{
+ struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
+
+ imx8_disable_clocks(sdev, priv->clks);
+ platform_device_unregister(priv->ipc_dev);
+
+ return 0;
+}
+
+/* on i.MX8 there is 1 to 1 match between type and BAR idx */
+static int imx8ulp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+ return type;
+}
+
+static int imx8ulp_suspend(struct snd_sof_dev *sdev)
+{
+ int i;
+ struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
+
+ /*Stall DSP, release in .run() */
+ regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_free_channel(priv->dsp_ipc, i);
+
+ imx8_disable_clocks(sdev, priv->clks);
+
+ return 0;
+}
+
+static int imx8ulp_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
+ int i;
+
+ imx8_enable_clocks(sdev, priv->clks);
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_request_channel(priv->dsp_ipc, i);
+
+ return 0;
+}
+
+static int imx8ulp_dsp_runtime_resume(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D0,
+ .substate = 0,
+ };
+
+ imx8ulp_resume(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8ulp_dsp_runtime_suspend(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D3,
+ .substate = 0,
+ };
+
+ imx8ulp_suspend(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8ulp_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = target_state,
+ .substate = 0,
+ };
+
+ if (!pm_runtime_suspended(sdev->dev))
+ imx8ulp_suspend(sdev);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static int imx8ulp_dsp_resume(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_dsp_state = {
+ .state = SOF_DSP_PM_D0,
+ .substate = 0,
+ };
+
+ imx8ulp_resume(sdev);
+
+ if (pm_runtime_suspended(sdev->dev)) {
+ pm_runtime_disable(sdev->dev);
+ pm_runtime_set_active(sdev->dev);
+ pm_runtime_mark_last_busy(sdev->dev);
+ pm_runtime_enable(sdev->dev);
+ pm_runtime_idle(sdev->dev);
+ }
+
+ return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+}
+
+static struct snd_soc_dai_driver imx8ulp_dai[] = {
+ {
+ .name = "sai5",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 32,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 32,
+ },
+ },
+ {
+ .name = "sai6",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 32,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 32,
+ },
+ },
+};
+
+static int imx8ulp_dsp_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ sdev->dsp_power_state = *target_state;
+
+ return 0;
+}
+
+/* i.MX8 ops */
+static struct snd_sof_dsp_ops sof_imx8ulp_ops = {
+ /* probe and remove */
+ .probe = imx8ulp_probe,
+ .remove = imx8ulp_remove,
+ /* DSP core boot */
+ .run = imx8ulp_run,
+ .reset = imx8ulp_reset,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* Module IO */
+ .read64 = sof_io_read64,
+
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
+ /* ipc */
+ .send_msg = imx8ulp_send_msg,
+ .get_mailbox_offset = imx8ulp_get_mailbox_offset,
+ .get_window_offset = imx8ulp_get_window_offset,
+
+ .ipc_msg_data = sof_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
+
+ /* stream callbacks */
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
+
+ /* module loading */
+ .get_bar_index = imx8ulp_get_bar_index,
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* Debug information */
+ .dbg_dump = imx8_dump,
+
+ /* Firmware ops */
+ .dsp_arch_ops = &sof_xtensa_arch_ops,
+
+ /* DAI drivers */
+ .drv = imx8ulp_dai,
+ .num_drv = ARRAY_SIZE(imx8ulp_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_NO_PERIOD_WAKEUP,
+
+ /* PM */
+ .runtime_suspend = imx8ulp_dsp_runtime_suspend,
+ .runtime_resume = imx8ulp_dsp_runtime_resume,
+
+ .suspend = imx8ulp_dsp_suspend,
+ .resume = imx8ulp_dsp_resume,
+
+ .set_power_state = imx8ulp_dsp_set_power_state,
+};
+
+static struct sof_dev_desc sof_of_imx8ulp_desc = {
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "imx/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "imx/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-imx8ulp.ri",
+ },
+ .nocodec_tplg_filename = "sof-imx8ulp-nocodec.tplg",
+ .ops = &sof_imx8ulp_ops,
+};
+
+static const struct of_device_id sof_of_imx8ulp_ids[] = {
+ { .compatible = "fsl,imx8ulp-dsp", .data = &sof_of_imx8ulp_desc},
+ { }
+};
+MODULE_DEVICE_TABLE(of, sof_of_imx8ulp_ids);
+
+/* DT driver definition */
+static struct platform_driver snd_sof_of_imx8ulp_driver = {
+ .probe = sof_of_probe,
+ .remove = sof_of_remove,
+ .driver = {
+ .name = "sof-audio-of-imx8ulp",
+ .pm = &sof_of_pm,
+ .of_match_table = sof_of_imx8ulp_ids,
+ },
+};
+module_platform_driver(snd_sof_of_imx8ulp_driver);
+
+MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index eddfd77ad90f..671c3e02d7df 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -620,8 +620,13 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/*
* The memory used for IMR boot loses its content in deeper than S3 state
* We must not try IMR boot on next power up (as it will fail).
+ *
+ * In case of firmware crash or boot failure set the skip_imr_boot to true
+ * as well in order to try to re-load the firmware to do a 'cold' boot.
*/
- if (sdev->system_suspend_target > SOF_SUSPEND_S3)
+ if (sdev->system_suspend_target > SOF_SUSPEND_S3 ||
+ sdev->fw_state == SOF_FW_CRASHED ||
+ sdev->fw_state == SOF_FW_BOOT_FAILED)
hda->skip_imr_boot = true;
hda_sdw_int_enable(sdev, false);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index eb22eb3f6fee..98812d51b31c 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -177,14 +177,13 @@ int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
* - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
*/
if (imr_boot)
- target_status = HDA_DSP_ROM_FW_ENTERED;
+ target_status = FSR_STATE_FW_ENTERED;
else
- target_status = HDA_DSP_ROM_INIT;
+ target_status = FSR_STATE_INIT_DONE;
ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
chip->rom_status_reg, status,
- ((status & HDA_DSP_ROM_STS_MASK)
- == target_status),
+ (FSR_TO_STATE_CODE(status) == target_status),
HDA_DSP_REG_POLL_INTERVAL_US,
chip->rom_init_timeout *
USEC_PER_MSEC);
@@ -292,8 +291,7 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
chip->rom_status_reg, reg,
- ((reg & HDA_DSP_ROM_STS_MASK)
- == HDA_DSP_ROM_FW_ENTERED),
+ (FSR_TO_STATE_CODE(reg) == FSR_STATE_FW_ENTERED),
HDA_DSP_REG_POLL_INTERVAL_US,
HDA_DSP_BASEFW_TIMEOUT_US);
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 8639ea63a10d..6d4ecbe14adf 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -574,7 +574,7 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *le
chip = get_chip_info(sdev->pdata);
for (i = 0; i < HDA_EXT_ROM_STATUS_SIZE; i++) {
value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg + i * 0x4);
- len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value);
+ len += scnprintf(msg + len, sizeof(msg) - len, " 0x%x", value);
}
dev_printk(level, sdev->dev, "extended rom status: %s", msg);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 5ef3e8775e36..ba6feb1b0d3b 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -251,12 +251,6 @@
#define FSR_STATE_BRINGUP_FW_ENTERED FSR_STATE_FW_ENTERED
/* ROM status/error values */
-#define HDA_DSP_ROM_STS_MASK GENMASK(23, 0)
-#define HDA_DSP_ROM_INIT 0x1
-#define HDA_DSP_ROM_FW_MANIFEST_LOADED 0x3
-#define HDA_DSP_ROM_FW_FW_LOADED 0x4
-#define HDA_DSP_ROM_FW_ENTERED 0x5
-#define HDA_DSP_ROM_RFW_START 0xf
#define HDA_DSP_ROM_CSE_ERROR 40
#define HDA_DSP_ROM_CSE_WRONG_RESPONSE 41
#define HDA_DSP_ROM_IMR_TO_SMALL 42
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index ccc44ba3ad94..aac47cd007e8 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -159,6 +159,62 @@ static const struct sof_dev_desc adl_desc = {
.ops_init = sof_tgl_ops_init,
};
+static const struct sof_dev_desc rpls_desc = {
+ .machines = snd_soc_acpi_intel_rpl_machines,
+ .alt_machines = snd_soc_acpi_intel_rpl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &adls_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/rpl-s",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-rpl-s.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-rpl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+};
+
+static const struct sof_dev_desc rpl_desc = {
+ .machines = snd_soc_acpi_intel_rpl_machines,
+ .alt_machines = snd_soc_acpi_intel_rpl_sdw_machines,
+ .use_acpi_target_states = true,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &tgl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "intel/sof",
+ [SOF_INTEL_IPC4] = "intel/avs/rpl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "intel/sof-tplg",
+ [SOF_INTEL_IPC4] = "intel/avs-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-rpl.ri",
+ [SOF_INTEL_IPC4] = "dsp_basefw.bin",
+ },
+ .nocodec_tplg_filename = "sof-rpl-nocodec.tplg",
+ .ops = &sof_tgl_ops,
+ .ops_init = sof_tgl_ops_init,
+};
+
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE(0x8086, 0xa0c8), /* TGL-LP */
@@ -172,7 +228,7 @@ static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE(0x8086, 0x7ad0), /* ADL-S */
.driver_data = (unsigned long)&adls_desc},
{ PCI_DEVICE(0x8086, 0x7a50), /* RPL-S */
- .driver_data = (unsigned long)&adls_desc},
+ .driver_data = (unsigned long)&rpls_desc},
{ PCI_DEVICE(0x8086, 0x51c8), /* ADL-P */
.driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */
@@ -180,9 +236,9 @@ static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE(0x8086, 0x51c9), /* ADL-PS */
.driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x51ca), /* RPL-P */
- .driver_data = (unsigned long)&adl_desc},
+ .driver_data = (unsigned long)&rpl_desc},
{ PCI_DEVICE(0x8086, 0x51cb), /* RPL-P */
- .driver_data = (unsigned long)&adl_desc},
+ .driver_data = (unsigned long)&rpl_desc},
{ PCI_DEVICE(0x8086, 0x51cc), /* ADL-M */
.driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x54c8), /* ADL-N */
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index b2cc046b9f60..65923e7a5976 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2338,7 +2338,7 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
}
dev_info(scomp->dev,
- "Topology: ABI %d:%d:%d Kernel ABI %hhu:%hhu:%hhu\n",
+ "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
man->priv.data[0], man->priv.data[1], man->priv.data[2],
SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index 9fadae8fd011..8bd2132b4f41 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -40,6 +40,17 @@ static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev)
ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data;
+ /*
+ * At the start of the firmware image we must have an extended manifest.
+ * Verify that the magic number is correct.
+ */
+ if (ext_man_hdr->id != SOF_EXT_MAN4_MAGIC_NUMBER) {
+ dev_err(sdev->dev,
+ "Unexpected extended manifest magic number: %#x\n",
+ ext_man_hdr->id);
+ return -EINVAL;
+ }
+
fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset;
if (!fw_hdr_offset)
return -EINVAL;
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index e006532caf2f..a1be5d74f40b 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -460,13 +460,36 @@ static int mt8186_get_bar_index(struct snd_sof_dev *sdev, u32 type)
return type;
}
-static int mt8186_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- void *p, size_t sz)
+static struct snd_soc_dai_driver mt8186_dai[] = {
{
- sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
- return 0;
-}
+ .name = "SOF_DL1",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_DL2",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_UL1",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+{
+ .name = "SOF_UL2",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+},
+};
/* mt8186 ops */
static struct snd_sof_dsp_ops sof_mt8186_ops = {
@@ -481,6 +504,10 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = {
.block_read = sof_block_read,
.block_write = sof_block_write,
+ /* Mailbox IO */
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
/* Register IO */
.write = sof_io_write,
.read = sof_io_read,
@@ -491,18 +518,26 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = {
.send_msg = mt8186_send_msg,
.get_mailbox_offset = mt8186_get_mailbox_offset,
.get_window_offset = mt8186_get_window_offset,
- .ipc_msg_data = mt8186_ipc_msg_data,
+ .ipc_msg_data = sof_ipc_msg_data,
.set_stream_data_offset = sof_set_stream_data_offset,
/* misc */
.get_bar_index = mt8186_get_bar_index,
+ /* stream callbacks */
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
+
/* firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
/* Firmware ops */
.dsp_arch_ops = &sof_xtensa_arch_ops,
+ /* DAI drivers */
+ .drv = mt8186_dai,
+ .num_drv = ARRAY_SIZE(mt8186_dai),
+
/* PM */
.suspend = mt8186_dsp_suspend,
.resume = mt8186_dsp_resume,
@@ -515,7 +550,16 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = {
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
};
+static struct snd_sof_of_mach sof_mt8186_machs[] = {
+ {
+ .compatible = "mediatek,mt8186",
+ .sof_tplg_filename = "sof-mt8186.tplg",
+ },
+ {}
+};
+
static const struct sof_dev_desc sof_of_mt8186_desc = {
+ .of_machines = sof_mt8186_machs,
.ipc_supported_mask = BIT(SOF_IPC),
.ipc_default = SOF_IPC,
.default_fw_path = {
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 6cb6a432be5e..49f7cb049f62 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -13,6 +13,7 @@
#include <linux/pm_runtime.h>
#include <sound/pcm_params.h>
#include <sound/sof.h>
+#include "sof-of-dev.h"
#include "sof-priv.h"
#include "sof-audio.h"
#include "sof-utils.h"
@@ -655,7 +656,12 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
struct snd_sof_pdata *plat_data = sdev->pdata;
const char *drv_name;
- drv_name = plat_data->machine->drv_name;
+ if (plat_data->machine)
+ drv_name = plat_data->machine->drv_name;
+ else if (plat_data->of_machine)
+ drv_name = plat_data->of_machine->drv_name;
+ else
+ drv_name = NULL;
pd->name = "sof-audio-component";
pd->probe = sof_pcm_probe;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 28976098a89e..c18e723435bd 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -10,6 +10,7 @@
#include <linux/bitfield.h>
#include "sof-audio.h"
+#include "sof-of-dev.h"
#include "ops.h"
static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h
index fd950a222ba4..2948b3a0d9fe 100644
--- a/sound/soc/sof/sof-of-dev.h
+++ b/sound/soc/sof/sof-of-dev.h
@@ -9,6 +9,13 @@
#ifndef __SOUND_SOC_SOF_OF_H
#define __SOUND_SOC_SOF_OF_H
+struct snd_sof_of_mach {
+ const char *compatible;
+ const char *drv_name;
+ const char *fw_filename;
+ const char *sof_tplg_filename;
+};
+
extern const struct dev_pm_ops sof_of_pm;
int sof_of_probe(struct platform_device *pdev);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 823583086279..33165299a20f 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -105,6 +105,13 @@ enum sof_debugfs_access_type {
SOF_DEBUGFS_ACCESS_D0_ONLY,
};
+struct sof_compr_stream {
+ u64 copied_total;
+ u32 sampling_rate;
+ u16 channels;
+ u16 sample_container_bytes;
+};
+
struct snd_sof_dev;
struct snd_sof_ipc_msg;
struct snd_sof_ipc;
diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c
index 7e8179cae92e..8163f453bf36 100644
--- a/sound/soc/ti/omap-mcbsp-st.c
+++ b/sound/soc/ti/omap-mcbsp-st.c
@@ -244,10 +244,10 @@ static ssize_t st_taps_show(struct device *dev,
spin_lock_irq(&mcbsp->lock);
for (i = 0; i < st_data->nr_taps; i++)
- status += sprintf(&buf[status], (i ? ", %d" : "%d"),
- st_data->taps[i]);
+ status += sysfs_emit_at(buf, status, (i ? ", %d" : "%d"),
+ st_data->taps[i]);
if (i)
- status += sprintf(&buf[status], "\n");
+ status += sysfs_emit_at(buf, status, "\n");
spin_unlock_irq(&mcbsp->lock);
return status;
diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c
index c4ac1f30b9fe..0b377bb7737f 100644
--- a/sound/soc/ti/omap-mcbsp.c
+++ b/sound/soc/ti/omap-mcbsp.c
@@ -517,7 +517,7 @@ static ssize_t prop##_show(struct device *dev, \
{ \
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
\
- return sprintf(buf, "%u\n", mcbsp->prop); \
+ return sysfs_emit(buf, "%u\n", mcbsp->prop); \
} \
\
static ssize_t prop##_store(struct device *dev, \
@@ -560,11 +560,11 @@ static ssize_t dma_op_mode_show(struct device *dev,
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
if (dma_op_mode == i)
- len += sprintf(buf + len, "[%s] ", *s);
+ len += sysfs_emit_at(buf, len, "[%s] ", *s);
else
- len += sprintf(buf + len, "%s ", *s);
+ len += sysfs_emit_at(buf, len, "%s ", *s);
}
- len += sprintf(buf + len, "\n");
+ len += sysfs_emit_at(buf, len, "\n");
return len;
}