ASoC: codecs: wcd93xxx: remove code duplication

Merge series from Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>:

All the Qualcomm WCD codecs and WCD based codecs have lots of code in
common, resulting in lot of duplicate code.
This series is an attempt to clean some of this by moving the common
code to wcd-common library or to soundwire helper functions.

Currently I have done cleanups for 4 codecs wcd934x, wcd937x, wcd938x
and wcd939x, however any new Qualcomm codecs can avoid this duplication
by using the wcd-common library.

I have also added two helpers of_sdw_find_device_by_node() and
sdw_slave_get_current_bank() in soundwire layer for the codecs to use them,
this series was already acked by Soundwire maintainer Vinod.

As original cleanup series was depending on the new soundwire interfaces
I have combined both the series as they are cleaning up code
duplications.

As am touching the same codec drivers, 2 bug fixes are also added at the
start of the series.

There is still lot of code that is duplicate, but this is just a
starting point for such cleanups.

I have tested this on T14s, any testing is appreciated.
diff --git a/Documentation/devicetree/bindings/mfd/twl4030-audio.txt b/Documentation/devicetree/bindings/mfd/twl4030-audio.txt
deleted file mode 100644
index 414d2ae..0000000
--- a/Documentation/devicetree/bindings/mfd/twl4030-audio.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-Texas Instruments TWL family (twl4030) audio module
-
-The audio module inside the TWL family consist of an audio codec and a vibra
-driver.
-
-Required properties:
-- compatible : must be "ti,twl4030-audio"
-
-Optional properties, nodes:
-
-Audio functionality:
-- codec { }: Need to be present if the audio functionality is used. Within this
-	     section the following options can be used:
-- ti,digimic_delay: Delay need after enabling the digimic to reduce artifacts
-		    from the start of the recorded sample (in ms)
--ti,ramp_delay_value: HS ramp delay configuration to reduce pop noise
--ti,hs_extmute: Use external mute for HS pop reduction
--ti,hs_extmute_gpio: Use external GPIO to control the external mute
--ti,offset_cncl_path: Offset cancellation path selection, refer to TRM for the
-		      valid values.
-
-Vibra functionality
-- ti,enable-vibra: Need to be set to <1> if the vibra functionality is used. if
-		   missing or it is 0, the vibra functionality is disabled.
-
-Example:
-&i2c1 {
-	clock-frequency = <2600000>;
-
-	twl: twl@48 {
-		reg = <0x48>;
-		interrupts = <7>; /* SYS_NIRQ cascaded to intc */
-		interrupt-parent = <&intc>;
-
-		twl_audio: audio {
-			compatible = "ti,twl4030-audio";
-
-			ti,enable-vibra = <1>;
-
-			codec {
-				ti,ramp_delay_value = <3>;
-			};
-
-		};
-	};
-};
diff --git a/Documentation/devicetree/bindings/sound/alc5623.txt b/Documentation/devicetree/bindings/sound/alc5623.txt
deleted file mode 100644
index 26c86c9..0000000
--- a/Documentation/devicetree/bindings/sound/alc5623.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-ALC5621/ALC5622/ALC5623 audio Codec
-
-Required properties:
-
- - compatible:	"realtek,alc5623"
- - reg:		the I2C address of the device.
-
-Optional properties:
-
- - add-ctrl:	  Default register value for Reg-40h, Additional Control
-		  Register. If absent or has the value of 0, the
-		  register is untouched.
-
- - jack-det-ctrl: Default register value for Reg-5Ah, Jack Detect
-		  Control Register. If absent or has value 0, the
-		  register is untouched.
-
-Example:
-
-	alc5621: alc5621@1a {
-		compatible = "alc5621";
-		reg = <0x1a>;
-		add-ctrl = <0x3700>;
-		jack-det-ctrl = <0x4810>;
-	};
diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
index 4477f84..1fdbeec 100644
--- a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
+++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4458.yaml
@@ -15,6 +15,9 @@
       - asahi-kasei,ak4458
       - asahi-kasei,ak4497
 
+  "#sound-dai-cells":
+    const: 0
+
   reg:
     maxItems: 1
 
@@ -46,6 +49,7 @@
   - reg
 
 allOf:
+  - $ref: dai-common.yaml#
   - if:
       properties:
         compatible:
diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt
deleted file mode 100644
index 7bb0362..0000000
--- a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-* Broadcom BCM2835 SoC I2S/PCM module
-
-Required properties:
-- compatible: "brcm,bcm2835-i2s"
-- reg: Should contain PCM registers location and length.
-- clocks: the (PCM) clock to use
-- dmas: List of DMA controller phandle and DMA request line ordered pairs.
-- dma-names: Identifier string for each DMA request line in the dmas property.
-  These strings correspond 1:1 with the ordered pairs in dmas.
-
-  One of the DMA channels will be responsible for transmission (should be
-  named "tx") and one for reception (should be named "rx").
-
-Example:
-
-bcm2835_i2s: i2s@7e203000 {
-	compatible = "brcm,bcm2835-i2s";
-	reg = <0x7e203000 0x24>;
-	clocks = <&clocks BCM2835_CLOCK_PCM>;
-
-	dmas = <&dma 2>,
-	       <&dma 3>;
-	dma-names = "tx", "rx";
-};
diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml
new file mode 100644
index 0000000..f3cfe92
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/brcm,bcm2835-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom BCM2835 SoC I2S/PCM module
+
+maintainers:
+  - Florian Fainelli <florian.fainelli@broadcom.com>
+
+properties:
+  compatible:
+    const: brcm,bcm2835-i2s
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  dmas:
+    items:
+      - description: Transmission DMA controller phandle and request line.
+      - description: Reception DMA controller phandle and request line.
+
+  dma-names:
+    items:
+      - const: tx
+      - const: rx
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - dmas
+  - dma-names
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/bcm2835.h>
+
+    i2s@7e203000 {
+        compatible = "brcm,bcm2835-i2s";
+        reg = <0x7e203000 0x24>;
+        clocks = <&clocks BCM2835_CLOCK_PCM>;
+        dmas = <&dma 2>, <&dma 3>;
+        dma-names = "tx", "rx";
+    };
diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml
index e4b2eb5..81a0215 100644
--- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml
+++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml
@@ -12,6 +12,22 @@
   - Matteo Martelli <matteomartelli3@gmail.com>
   - Binbin Zhou <zhoubinbin@loongson.cn>
 
+description: |
+  Everest ES8311, ES8316 and ES8323 audio CODECs
+
+  Pins on the device (for linking into audio routes):
+
+    Outputs:
+      * LOUT:     Left Analog Output
+      * ROUT:     Right Analog Output
+      * MICBIAS:  Microphone Bias
+
+    Inputs:
+      * MIC1P:    Microphone 1 Positive Analog Input
+      * MIC1N:    Microphone 1 Negative Analog Input
+      * MIC2P:    Microphone 2 Positive Analog Input
+      * MIC2N:    Microphone 2 Negative Analog Input
+
 allOf:
   - $ref: dai-common.yaml#
 
diff --git a/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
new file mode 100644
index 0000000..4da7353
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/foursemi,fs2105s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: FourSemi FS2104/5S Digital Audio Amplifier
+
+maintainers:
+  - Nick Li <nick.li@foursemi.com>
+
+description:
+  The FS2104 is a 15W Inductor-Less, Stereo, Closed-Loop,
+  Digital Input Class-D Power Amplifier with Enhanced Signal Processing.
+  The FS2105S is a 30W Inductor-Less, Stereo, Closed-Loop,
+  Digital Input Class-D Power Amplifier with Enhanced Signal Processing.
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - foursemi,fs2104
+          - const: foursemi,fs2105s
+      - enum:
+          - foursemi,fs2105s
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: The clock of I2S BCLK
+
+  clock-names:
+    items:
+      - const: bclk
+
+  interrupts:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 0
+
+  pvdd-supply:
+    description:
+      Regulator for power supply(PVDD in datasheet).
+
+  dvdd-supply:
+    description:
+      Regulator for digital supply(DVDD in datasheet).
+
+  reset-gpios:
+    maxItems: 1
+    description:
+      It's the SDZ pin in datasheet, the pin is active low,
+      it will power down and reset the chip to shut down state.
+
+  firmware-name:
+    maxItems: 1
+    description: |
+      The firmware(*.bin) contains:
+      a. Register initialization settings
+      b. DSP effect parameters
+      c. Multi-scene sound effect configurations(optional)
+      It's gernerated by FourSemi's tuning tool.
+
+required:
+  - compatible
+  - reg
+  - '#sound-dai-cells'
+  - pvdd-supply
+  - dvdd-supply
+  - reset-gpios
+  - firmware-name
+
+allOf:
+  - $ref: dai-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        audio-codec@68 {
+            compatible = "foursemi,fs2105s";
+            reg = <0x68>;
+            clocks = <&clocks 18>;
+            clock-names = "bclk";
+            #sound-dai-cells = <0>;
+            pvdd-supply = <&pvdd_supply>;
+            dvdd-supply = <&dvdd_supply>;
+            reset-gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+            firmware-name = "fs2105s-btl-2p0-0s.bin";
+            pinctrl-names = "default";
+            pinctrl-0 = <&fs210x_pins_default>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
index 8f1108e..d5727f8 100644
--- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
@@ -104,6 +104,6 @@
                     "ctx2_rx", "ctx2_tx",
                     "ctx3_rx", "ctx3_tx";
         firmware-name = "imx/easrc/easrc-imx8mn.bin";
-        fsl,asrc-rate  = <8000>;
+        fsl,asrc-rate = <8000>;
         fsl,asrc-format = <2>;
     };
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
index 85799f8..c9152ba 100644
--- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
@@ -176,7 +176,7 @@
                <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
         dma-names = "rxa", "rxb", "rxc",
                     "txa", "txb", "txc";
-        fsl,asrc-rate  = <48000>;
+        fsl,asrc-rate = <48000>;
         fsl,asrc-width = <16>;
 
         port {
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
deleted file mode 100644
index 2f89db8..0000000
--- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-Freescale i.MX audio complex with SGTL5000 codec
-
-Required properties:
-
-  - compatible		: "fsl,imx-audio-sgtl5000"
-
-  - model		: The user-visible name of this sound complex
-
-  - ssi-controller	: The phandle of the i.MX SSI controller
-
-  - audio-codec		: The phandle of the SGTL5000 audio codec
-
-  - audio-routing	: A list of the connections between audio components.
-			  Each entry is a pair of strings, the first being the
-			  connection's sink, the second being the connection's
-			  source. Valid names could be power supplies, SGTL5000
-			  pins, and the jacks on the board:
-
-			  Power supplies:
-			   * Mic Bias
-
-			  SGTL5000 pins:
-			   * MIC_IN
-			   * LINE_IN
-			   * HP_OUT
-			   * LINE_OUT
-
-			  Board connectors:
-			   * Mic Jack
-			   * Line In Jack
-			   * Headphone Jack
-			   * Line Out Jack
-			   * Ext Spk
-
-  - mux-int-port	: The internal port of the i.MX audio muxer (AUDMUX)
-
-  - mux-ext-port	: The external port of the i.MX audio muxer
-
-Note: The AUDMUX port numbering should start at 1, which is consistent with
-hardware manual.
-
-Example:
-
-sound {
-	compatible = "fsl,imx51-babbage-sgtl5000",
-		     "fsl,imx-audio-sgtl5000";
-	model = "imx51-babbage-sgtl5000";
-	ssi-controller = <&ssi1>;
-	audio-codec = <&sgtl5000>;
-	audio-routing =
-		"MIC_IN", "Mic Jack",
-		"Mic Jack", "Mic Bias",
-		"Headphone Jack", "HP_OUT";
-	mux-int-port = <1>;
-	mux-ext-port = <3>;
-};
diff --git a/Documentation/devicetree/bindings/sound/linux,spdif.yaml b/Documentation/devicetree/bindings/sound/linux,spdif.yaml
index 0f4893e..aea6230 100644
--- a/Documentation/devicetree/bindings/sound/linux,spdif.yaml
+++ b/Documentation/devicetree/bindings/sound/linux,spdif.yaml
@@ -23,6 +23,9 @@
 
   sound-name-prefix: true
 
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+
 required:
   - "#sound-dai-cells"
   - compatible
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
index a54f194..4ebbcb4 100644
--- a/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8825.yaml
@@ -9,6 +9,20 @@
 maintainers:
   - John Hsu <KCHSU0@nuvoton.com>
 
+description: |
+  NAU8825 audio CODEC
+
+  Pins on the device (for linking into audio routes):
+
+    Outputs:
+      * HPOL    : Headphone Left Output
+      * HPOR    : Headphone Right Output
+      * MICBIAS : Microphone Bias Output
+
+    Inputs:
+      * MICP    : Analog Microphone Positive Input
+      * MICN    : Analog Microphone Negative Input
+
 allOf:
   - $ref: dai-common.yaml#
 
diff --git a/Documentation/devicetree/bindings/sound/omap-twl4030.txt b/Documentation/devicetree/bindings/sound/omap-twl4030.txt
deleted file mode 100644
index f6a715e..0000000
--- a/Documentation/devicetree/bindings/sound/omap-twl4030.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-* Texas Instruments SoC with twl4030 based audio setups
-
-Required properties:
-- compatible: "ti,omap-twl4030"
-- ti,model: Name of the sound card (for example "omap3beagle")
-- ti,mcbsp: phandle for the McBSP node
-
-Optional properties:
-- ti,codec: phandle for the twl4030 audio node
-- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl
-- ti, jack-det-gpio: Jack detect GPIO
-- ti,audio-routing: List of connections between audio components.
-  Each entry is a pair of strings, the first being the connection's sink,
-  the second being the connection's source.
-  If the routing is not provided all possible connection will be available
-
-Available audio endpoints for the audio-routing table:
-
-Board connectors:
- * Headset Stereophone
- * Earpiece Spk
- * Handsfree Spk
- * Ext Spk
- * Main Mic
- * Sub Mic
- * Headset Mic
- * Carkit Mic
- * Digital0 Mic
- * Digital1 Mic
- * Line In
-
-twl4030 pins:
- * HSOL
- * HSOR
- * EARPIECE
- * HFL
- * HFR
- * PREDRIVEL
- * PREDRIVER
- * CARKITL
- * CARKITR
- * MAINMIC
- * SUBMIC
- * HSMIC
- * DIGIMIC0
- * DIGIMIC1
- * CARKITMIC
- * AUXL
- * AUXR
-
- * Headset Mic Bias
- * Mic Bias 1 /* Used for Main Mic or Digimic0 */
- * Mic Bias 2 /* Used for Sub Mic or Digimic1 */
-
-Example:
-
-sound {
-	compatible = "ti,omap-twl4030";
-	ti,model = "omap3beagle";
-
-	ti,mcbsp = <&mcbsp2>;
-};
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
index dd549db..1c0d78a 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -20,6 +20,7 @@
           - qcom,sc8280xp-lpass-va-macro
       - items:
           - enum:
+              - qcom,glymur-lpass-va-macro
               - qcom,sm8650-lpass-va-macro
               - qcom,sm8750-lpass-va-macro
               - qcom,x1e80100-lpass-va-macro
@@ -79,12 +80,25 @@
         compatible:
           contains:
             const: qcom,sc7280-lpass-va-macro
+
     then:
-      properties:
-        clocks:
-          maxItems: 1
-        clock-names:
-          maxItems: 1
+      if:
+        required:
+          - power-domains
+      then:
+        properties:
+          clocks:
+            maxItems: 1
+          clock-names:
+            maxItems: 1
+      else:
+        properties:
+          clocks:
+            minItems: 3
+            maxItems: 3
+          clock-names:
+            minItems: 3
+            maxItems: 3
 
   - if:
       properties:
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
index 9082e36..b6f5ba5 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
@@ -20,6 +20,7 @@
           - qcom,sc8280xp-lpass-wsa-macro
       - items:
           - enum:
+              - qcom,glymur-lpass-wsa-macro
               - qcom,sm8650-lpass-wsa-macro
               - qcom,sm8750-lpass-wsa-macro
               - qcom,x1e80100-lpass-wsa-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
new file mode 100644
index 0000000..6e2f103
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
@@ -0,0 +1,134 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,pm4125-codec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm PM4125 Audio Codec
+
+maintainers:
+  - Alexey Klimov <alexey.klimov@linaro.org>
+
+description:
+  The audio codec IC found on Qualcomm PM4125/PM2250 PMIC.
+  It has RX and TX Soundwire slave devices.
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    const: qcom,pm4125-codec
+
+  reg:
+    description:
+      Specifies the SPMI base address for the audio codec peripherals. The
+      address space contains reset register needed to power-on the codec.
+    maxItems: 1
+
+  reg-names:
+    maxItems: 1
+
+  vdd-io-supply:
+    description: A reference to the 1.8V I/O supply
+
+  vdd-cp-supply:
+    description: A reference to the charge pump I/O supply
+
+  vdd-mic-bias-supply:
+    description: A reference to the 3.3V mic bias supply
+
+  vdd-pa-vpos-supply:
+    description: A reference to the PA VPOS supply
+
+  qcom,tx-device:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: A reference to Soundwire tx device phandle
+
+  qcom,rx-device:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: A reference to Soundwire rx device phandle
+
+  qcom,micbias1-microvolt:
+    description: micbias1 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,micbias2-microvolt:
+    description: micbias2 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,micbias3-microvolt:
+    description: micbias3 voltage
+    minimum: 1800000
+    maximum: 2850000
+
+  qcom,mbhc-buttons-vthreshold-microvolt:
+    description:
+      Array of 8 Voltage threshold values corresponding to headset
+      button0 - button7
+    minItems: 8
+    maxItems: 8
+
+  '#sound-dai-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - vdd-io-supply
+  - vdd-cp-supply
+  - vdd-mic-bias-supply
+  - vdd-pa-vpos-supply
+  - qcom,tx-device
+  - qcom,rx-device
+  - qcom,micbias1-microvolt
+  - qcom,micbias2-microvolt
+  - qcom,micbias3-microvolt
+  - '#sound-dai-cells'
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/spmi/spmi.h>
+
+    spmi {
+        #address-cells = <2>;
+        #size-cells = <0>;
+
+        pmic {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            audio-codec@f000 {
+                compatible = "qcom,pm4125-codec";
+                reg = <0xf000>;
+                vdd-io-supply = <&pm4125_l15>;
+                vdd-cp-supply = <&pm4125_s4>;
+                vdd-pa-vpos-supply = <&pm4125_s4>;
+                vdd-mic-bias-supply = <&pm4125_l22>;
+                qcom,micbias1-microvolt = <1800000>;
+                qcom,micbias2-microvolt = <1800000>;
+                qcom,micbias3-microvolt = <1800000>;
+                qcom,rx-device = <&pm4125_rx>;
+                qcom,tx-device = <&pm4125_tx>;
+                #sound-dai-cells = <1>;
+            };
+        };
+    };
+
+    /* ... */
+
+    soundwire@a610000 {
+        reg = <0x0a610000 0x2000>;
+        #address-cells = <2>;
+        #size-cells = <0>;
+        pm4125_rx: audio-codec@0,4 {
+            compatible = "sdw20217010c00";
+            reg = <0 4>;
+            qcom,rx-port-mapping = <1 3>;
+        };
+    };
+...
diff --git a/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
new file mode 100644
index 0000000..23624f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,pm4125-sdw.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SoundWire Slave devices on PM4125/PM2250 PMIC audio codec.
+
+maintainers:
+  - Alexey Klimov <alexey.klimov@linaro.org>
+
+description:
+  The audio codec IC found on Qualcomm PM4125/PM2250 PMICs.
+  It has RX and TX Soundwire slave devices.
+
+properties:
+  compatible:
+    const: sdw20217010c00
+
+  reg:
+    maxItems: 1
+
+  qcom,tx-port-mapping:
+    description: |
+      Specifies static port mapping between device and host tx ports.
+      In the order of the device port index which are adc1_port, adc23_port,
+      dmic03_mbhc_port, dmic46_port.
+      Supports maximum 2 tx soundwire ports.
+
+      PM4125 TX Port 1 (ADC1,2 & DMIC0 & MBHC)    <=> SWR0 Port 1
+      PM4125 TX Port 2 (ADC1 & DMIC0,1,2 & MBHC)  <=> SWR0 Port 2
+
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 2
+    maxItems: 2
+    items:
+      enum: [1, 2, 3, 4]
+
+  qcom,rx-port-mapping:
+    description: |
+      Specifies static port mapping between device and host rx ports.
+      In the order of device port index which are hph_port, clsh_port,
+      comp_port, lo_port, dsd port.
+      Supports maximum 2 rx soundwire ports.
+
+      PM4125 RX Port 1 (HPH_L/R)       <==>    SWR1 Port 1 (HPH_L/R)
+      PM4125 RX Port 2 (COMP_L/R)      <==>    SWR1 Port 3 (COMP_L/R)
+
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 2
+    maxItems: 2
+    items:
+      enum: [1, 2, 3, 4, 5]
+
+required:
+  - compatible
+  - reg
+
+oneOf:
+  - required:
+      - qcom,tx-port-mapping
+  - required:
+      - qcom,rx-port-mapping
+
+additionalProperties: false
+
+examples:
+  - |
+    soundwire@a610000 {
+        reg = <0x0a610000 0x2000>;
+        #address-cells = <2>;
+        #size-cells = <0>;
+        pm4125_rx: codec@0,1 {
+            compatible = "sdw20217010c00";
+            reg = <0 1>;
+            qcom,rx-port-mapping = <1 3>;
+        };
+    };
+...
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index 5d3dbb6..8ac9162 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -31,6 +31,7 @@
           - fairphone,fp4-sndcard
           - fairphone,fp5-sndcard
           - qcom,apq8096-sndcard
+          - qcom,glymur-sndcard
           - qcom,qcm6490-idp-sndcard
           - qcom,qcs6490-rb3gen2-sndcard
           - qcom,qcs8275-sndcard
diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml
index 14d312f..098f1df 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml
@@ -29,6 +29,10 @@
     description: GPIO spec for Powerdown/Shutdown line to use (pin SD_N)
     maxItems: 1
 
+  reset-gpios:
+    description: Powerdown/Shutdown line to use (pin SD_N)
+    maxItems: 1
+
   vdd-supply:
     description: VDD Supply for the Codec
 
@@ -50,10 +54,15 @@
   - compatible
   - reg
   - vdd-supply
-  - powerdown-gpios
   - "#thermal-sensor-cells"
   - "#sound-dai-cells"
 
+oneOf:
+  - required:
+      - powerdown-gpios
+  - required:
+      - reset-gpios
+
 unevaluatedProperties: false
 
 examples:
diff --git a/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml b/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml
new file mode 100644
index 0000000..683c58c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/realtek,alc5623.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ALC5621/ALC5623 Audio Codec
+
+maintainers:
+  - Mahdi Khosravi <mmk1776@gmail.com>
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    enum:
+      - realtek,alc5621
+      - realtek,alc5623
+
+  reg:
+    maxItems: 1
+
+  add-ctrl:
+    description:
+      Default register value for Reg-40h, Additional Control Register.
+      If absent or zero, the register is left untouched.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+  jack-det-ctrl:
+    description:
+      Default register value for Reg-5Ah, Jack Detect Control Register.
+      If absent or zero, the register is left untouched.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        codec@1a {
+            compatible = "realtek,alc5623";
+            reg = <0x1a>;
+            add-ctrl = <0x3700>;
+            jack-det-ctrl = <0x4810>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml b/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
new file mode 100644
index 0000000..27c7019
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,omap-twl4030.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,omap-twl4030.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments SoC with twl4030 based audio setups
+
+maintainers:
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
+
+description:
+  Audio setups on TI OMAP SoCs using TWL4030-family
+  audio codec connected via a McBSP port.
+
+properties:
+  compatible:
+    const: ti,omap-twl4030
+
+  ti,model:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: Name of the sound card (for example "omap3beagle").
+
+  ti,mcbsp:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: phandle for the McBSP node.
+
+  ti,codec:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: phandle for the twl4030 audio node.
+
+  ti,mcbsp-voice:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: phandle to the McBSP node connected to the voice port.
+
+  ti,jack-det-gpio:
+    description: GPIO specifier for jack detection.
+    maxItems: 1
+
+  ti,audio-routing:
+    description: |
+      A list of audio routing connections. Each entry is a pair of strings,
+      with the first being the connection's sink and the second being the
+      source. If not provided, all possible connections are available.
+
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    items:
+      enum:
+        # Board Connectors
+        - Headset Stereophone
+        - Earpiece Spk
+        - Handsfree Spk
+        - Ext Spk
+        - Main Mic
+        - Sub Mic
+        - Headset Mic
+        - Carkit Mic
+        - Digital0 Mic
+        - Digital1 Mic
+        - Line In
+
+        # CODEC Pins
+        - HSOL
+        - HSOR
+        - EARPIECE
+        - HFL
+        - HFR
+        - PREDRIVEL
+        - PREDRIVER
+        - CARKITL
+        - CARKITR
+        - MAINMIC
+        - SUBMIC
+        - HSMIC
+        - DIGIMIC0
+        - DIGIMIC1
+        - CARKITMIC
+        - AUXL
+        - AUXR
+
+        # Headset Mic Bias
+        - Mic Bias 1   # Used for Main Mic or Digimic0
+        - Mic Bias 2   # Used for Sub Mic or Digimic1
+
+required:
+  - compatible
+  - ti,model
+  - ti,mcbsp
+
+additionalProperties: false
+
+examples:
+  - |
+    sound {
+        compatible = "ti,omap-twl4030";
+        ti,model = "omap3beagle";
+        ti,mcbsp = <&mcbsp2>;
+    };
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm1754.yaml b/Documentation/devicetree/bindings/sound/ti,pcm1754.yaml
new file mode 100644
index 0000000..a757f73
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,pcm1754.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,pcm1754.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments PCM1754 Stereo DAC
+
+description:
+  The PCM1754 is a simple stereo DAC that is controlled via hardware gpios.
+
+maintainers:
+  - Stefan Kerkmann <s.kerkmann@pengutronix.de>
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    enum:
+      - ti,pcm1754
+
+  vcc-supply: true
+
+  '#sound-dai-cells':
+    const: 0
+
+  format-gpios:
+    maxItems: 1
+    description:
+      GPIO used to select the PCM format
+
+  mute-gpios:
+    maxItems: 1
+    description:
+      GPIO used to mute all outputs
+
+required:
+  - compatible
+  - '#sound-dai-cells'
+  - vcc-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    codec {
+      compatible = "ti,pcm1754";
+      #sound-dai-cells = <0>;
+
+      vcc-supply = <&vcc_reg>;
+      mute-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+      format-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+    };
diff --git a/Documentation/devicetree/bindings/sound/ti,twl4030-audio.yaml b/Documentation/devicetree/bindings/sound/ti,twl4030-audio.yaml
new file mode 100644
index 0000000..c9c3f75
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,twl4030-audio.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,twl4030-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TWL4030-family Audio Module
+
+maintainers:
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
+
+description:
+  The audio module within the TWL4030-family of companion chips consists
+  of an audio codec and a vibra driver. This binding describes the parent
+  node for these functions.
+
+properties:
+  compatible:
+    const: ti,twl4030-audio
+
+  codec:
+    type: object
+    description: Node containing properties for the audio codec functionality.
+
+    properties:
+      ti,digimic_delay:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Delay in milliseconds after enabling digital microphones to reduce
+          artifacts.
+
+      ti,ramp_delay_value:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Headset ramp delay configuration to reduce pop noise.
+
+      ti,hs_extmute:
+        type: boolean
+        description:
+          Enable the use of an external mute for headset pop reduction.
+
+      ti,hs_extmute_gpio:
+        $ref: /schemas/types.yaml#/definitions/phandle-array
+        description:
+          The GPIO specifier for the external mute control.
+        maxItems: 1
+
+      ti,offset_cncl_path:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Offset cancellation path selection. Refer to the Technical
+          Reference Manual for valid values.
+
+  # The 'codec' node itself is optional, but if it exists, it can be empty.
+  # We don't require any of its sub-properties.
+
+  ti,enable-vibra:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1]
+    description:
+      Enable or disable the vibra functionality.
+
+additionalProperties: false
+
+required:
+  - compatible
+
+examples:
+  - |
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      twl: twl@48 {
+        reg = <0x48>;
+        interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+        interrupt-parent = <&intc>;
+
+        twl_audio: audio {
+          compatible = "ti,twl4030-audio";
+
+          ti,enable-vibra = <1>;
+
+          codec {
+            ti,ramp_delay_value = <3>;
+          };
+
+        };
+      };
+    };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
index 3c2b979..c8c786c 100644
--- a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
+++ b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml
@@ -9,6 +9,28 @@
 maintainers:
   - patches@opensource.cirrus.com
 
+description: |
+  Wolfson WM8960 audio codec
+
+  Pins on the device (for linking into audio routes):
+
+    Outputs:
+      * HP_L    : Left Headphone/Line Output
+      * HP_R    : Right Headphone/Line Output
+      * SPK_LP  : Left Speaker Output (Positive)
+      * SPK_LN  : Left Speaker Output (Negative)
+      * SPK_RP  : Right Speaker Output (Positive)
+      * SPK_RN  : Right Speaker Output (Negative)
+      * OUT3    : Mono, Left, Right or buffered midrail output for capless mode
+
+    Inputs:
+      * LINPUT1 : Left single-ended or negative differential microphone input
+      * RINPUT1 : Right single-ended or negative differential microphone input
+      * LINPUT2 : Left line input or positive differential microphone input
+      * RINPUT2 : Right line input or positive differential microphone input
+      * LINPUT3 : Left line input, positive differential microphone, or Jack Detect 2
+      * RINPUT3 : Right line input, positive differential microphone, or Jack Detect 3
+
 properties:
   compatible:
     const: wlf,wm8960
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 9ec8947..dc534d7 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -554,6 +554,8 @@
     description: FocalTech Systems Co.,Ltd
   "^forlinx,.*":
     description: Baoding Forlinx Embedded Technology Co., Ltd.
+  "^foursemi,.*":
+    description: Shanghai FourSemi Semiconductor Co.,Ltd.
   "^freebox,.*":
     description: Freebox SAS
   "^freecom,.*":
diff --git a/Documentation/sound/soc/codec.rst b/Documentation/sound/soc/codec.rst
index af973c4..b9d87a4 100644
--- a/Documentation/sound/soc/codec.rst
+++ b/Documentation/sound/soc/codec.rst
@@ -131,8 +131,8 @@
 	int (*prepare)(struct snd_pcm_substream *);
   };
 
-Please refer to the ALSA driver PCM documentation for details.
-https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html
+Please refer to the :doc:`ALSA driver PCM documentation
+<../kernel-api/writing-an-alsa-driver>` for details.
 
 
 DAPM description
diff --git a/Documentation/sound/soc/platform.rst b/Documentation/sound/soc/platform.rst
index 7036630..bd21d0a 100644
--- a/Documentation/sound/soc/platform.rst
+++ b/Documentation/sound/soc/platform.rst
@@ -45,8 +45,8 @@
 	...
   };
 
-Please refer to the ALSA driver documentation for details of audio DMA.
-https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html
+Please refer to the :doc:`ALSA driver documentation
+<../kernel-api/writing-an-alsa-driver>` for details of audio DMA.
 
 An example DMA driver is soc/pxa/pxa2xx-pcm.c
 
diff --git a/MAINTAINERS b/MAINTAINERS
index f620696..2356c46 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9592,6 +9592,14 @@
 K:	\bunsafe_memcpy\b
 K:	\b__NO_FORTIFY\b
 
+FOURSEMI AUDIO AMPLIFIER DRIVER
+M:	Nick Li <nick.li@foursemi.com>
+L:	linux-sound@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
+F:	sound/soc/codecs/fs-amp-lib.*
+F:	sound/soc/codecs/fs210x.*
+
 FPGA DFL DRIVERS
 M:	Xu Yilun <yilun.xu@intel.com>
 R:	Tom Rix <trix@redhat.com>
@@ -20470,6 +20478,8 @@
 F:	sound/soc/codecs/lpass-*.*
 F:	sound/soc/codecs/msm8916-wcd-analog.c
 F:	sound/soc/codecs/msm8916-wcd-digital.c
+F:	sound/soc/codecs/pm4125-sdw.c
+F:	sound/soc/codecs/pm4125.*
 F:	sound/soc/codecs/wcd-clsh-v2.*
 F:	sound/soc/codecs/wcd-mbhc-v2.*
 F:	sound/soc/codecs/wcd93*.*
diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h
index 5459c22..43a87a3 100644
--- a/include/sound/cs-amp-lib.h
+++ b/include/sound/cs-amp-lib.h
@@ -49,6 +49,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
 			    const struct cirrus_amp_cal_data *data);
 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
 				    struct cirrus_amp_cal_data *out_data);
+int cs_amp_get_vendor_spkid(struct device *dev);
 
 struct cs_amp_test_hooks {
 	efi_status_t (*get_efi_variable)(efi_char16_t *name,
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 1ef13bc..9472f0a 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -69,6 +69,10 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
  * @peripheral_config: peripheral configuration for programming peripheral
  * for dmaengine transfer
  * @peripheral_size: peripheral configuration buffer size
+ * @port_window_size: The length of the register area in words the data need
+ * to be accessed on the device side. It is only used for devices which is using
+ * an area instead of a single register to send/receive the data. Typically the
+ * DMA loops in this area in order to transfer the data.
  */
 struct snd_dmaengine_dai_dma_data {
 	dma_addr_t addr;
@@ -80,6 +84,7 @@ struct snd_dmaengine_dai_dma_data {
 	unsigned int flags;
 	void *peripheral_config;
 	size_t peripheral_size;
+	u32 port_window_size;
 };
 
 void snd_dmaengine_pcm_set_config_from_dai_data(
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index 2caa807..b954f34 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -261,89 +261,18 @@ struct snd_soc_component {
 	list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list)
 
 /**
- * snd_soc_dapm_to_component() - Casts a DAPM context to the component it is
- *  embedded in
- * @dapm: The DAPM context to cast to the component
- *
- * This function must only be used on DAPM contexts that are known to be part of
- * a component (e.g. in a component driver). Otherwise the behavior is
- * undefined.
- */
-static inline struct snd_soc_component *snd_soc_dapm_to_component(
-	struct snd_soc_dapm_context *dapm)
-{
-	return container_of(dapm, struct snd_soc_component, dapm);
-}
-
-/**
- * snd_soc_component_get_dapm() - Returns the DAPM context associated with a
+ * snd_soc_component_to_dapm() - Returns the DAPM context associated with a
  *  component
  * @component: The component for which to get the DAPM context
  */
-static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
+static inline struct snd_soc_dapm_context *snd_soc_component_to_dapm(
 	struct snd_soc_component *component)
 {
 	return &component->dapm;
 }
 
-/**
- * snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level
- * @component: The COMPONENT for which to initialize the DAPM bias level
- * @level: The DAPM level to initialize to
- *
- * Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level()
- */
-static inline void
-snd_soc_component_init_bias_level(struct snd_soc_component *component,
-				  enum snd_soc_bias_level level)
-{
-	snd_soc_dapm_init_bias_level(
-		snd_soc_component_get_dapm(component), level);
-}
-
-/**
- * snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level
- * @component: The COMPONENT for which to get the DAPM bias level
- *
- * Returns: The current DAPM bias level of the COMPONENT.
- */
-static inline enum snd_soc_bias_level
-snd_soc_component_get_bias_level(struct snd_soc_component *component)
-{
-	return snd_soc_dapm_get_bias_level(
-		snd_soc_component_get_dapm(component));
-}
-
-/**
- * snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level
- * @component: The COMPONENT for which to set the level
- * @level: The level to set to
- *
- * Forces the COMPONENT bias level to a specific state. See
- * snd_soc_dapm_force_bias_level().
- */
-static inline int
-snd_soc_component_force_bias_level(struct snd_soc_component *component,
-				   enum snd_soc_bias_level level)
-{
-	return snd_soc_dapm_force_bias_level(
-		snd_soc_component_get_dapm(component),
-		level);
-}
-
-/**
- * snd_soc_dapm_kcontrol_component() - Returns the component associated to a
- * kcontrol
- * @kcontrol: The kcontrol
- *
- * This function must only be used on DAPM contexts that are known to be part of
- * a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined
- */
-static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component(
-	struct snd_kcontrol *kcontrol)
-{
-	return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol));
-}
+// FIXME
+#define snd_soc_component_get_dapm	snd_soc_component_to_dapm
 
 /**
  * snd_soc_component_cache_sync() - Sync the register cache with the hardware
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 0b5c7e6..7594132 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -583,11 +583,9 @@ struct snd_soc_dapm_update {
 struct snd_soc_dapm_context {
 	enum snd_soc_bias_level bias_level;
 
-	/* bit field */
-	unsigned int idle_bias_off:1;		/* Use BIAS_OFF instead of STANDBY */
-	unsigned int suspend_bias_off:1;	/* Use BIAS_OFF in suspend if the DAPM is idle */
+	bool idle_bias;				/* Use BIAS_OFF instead of STANDBY when false */
 
-	struct device *dev;			/* from parent - for debug */
+	struct device *dev;			/* from parent - for debug */ /* REMOVE ME */
 	struct snd_soc_component *component;	/* parent component */
 	struct snd_soc_card *card;		/* parent card */
 
@@ -660,6 +658,12 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
 int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
 			    struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
 int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s);
+struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm);
+struct snd_soc_card *snd_soc_dapm_to_card(struct snd_soc_dapm_context *dapm);
+struct snd_soc_component *snd_soc_dapm_to_component(struct snd_soc_dapm_context *dapm);
+
+bool snd_soc_dapm_get_idle_bias(struct snd_soc_dapm_context *dapm);
+void snd_soc_dapm_set_idle_bias(struct snd_soc_dapm_context *dapm, bool on);
 
 /* dapm path setup */
 int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
@@ -699,7 +703,6 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin);
 int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin);
 int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin);
-unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
 void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card);
 
 /*
@@ -718,10 +721,23 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction));
 void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list);
 
-struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(struct snd_kcontrol *kcontrol);
-struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(struct snd_kcontrol *kcontrol);
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_to_dapm(struct snd_kcontrol *kcontrol);
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_to_widget(struct snd_kcontrol *kcontrol);
+struct snd_soc_component *snd_soc_dapm_kcontrol_to_component(struct snd_kcontrol *kcontrol);
+unsigned int snd_soc_dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
 
 int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level);
+enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm);
+void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level);
+
+// REMOVE ME !!
+#define snd_soc_component_force_bias_level(c, l)	snd_soc_dapm_force_bias_level(&(c)->dapm, l)
+#define snd_soc_component_get_bias_level(c)		snd_soc_dapm_get_bias_level(&(c)->dapm)
+#define snd_soc_component_init_bias_level(c, l)		snd_soc_dapm_init_bias_level(&(c)->dapm, l)
+#define snd_soc_dapm_kcontrol_widget			snd_soc_dapm_kcontrol_to_widget
+#define snd_soc_dapm_kcontrol_dapm			snd_soc_dapm_kcontrol_to_dapm
+#define dapm_kcontrol_get_value				snd_soc_dapm_kcontrol_get_value
+#define snd_soc_dapm_kcontrol_component			snd_soc_dapm_kcontrol_to_component
 
 #define for_each_dapm_widgets(list, i, widget)				\
 	for ((i) = 0;							\
@@ -729,37 +745,6 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_so
 	     (i)++)
 
 /**
- * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level
- * @dapm: The DAPM context to initialize
- * @level: The DAPM level to initialize to
- *
- * This function only sets the driver internal state of the DAPM level and will
- * not modify the state of the device. Hence it should not be used during normal
- * operation, but only to synchronize the internal state to the device state.
- * E.g. during driver probe to set the DAPM level to the one corresponding with
- * the power-on reset state of the device.
- *
- * To change the DAPM state of the device use snd_soc_dapm_set_bias_level().
- */
-static inline void snd_soc_dapm_init_bias_level(
-	struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
-{
-	dapm->bias_level = level;
-}
-
-/**
- * snd_soc_dapm_get_bias_level() - Get current DAPM bias level
- * @dapm: The context for which to get the bias level
- *
- * Returns: The current bias level of the passed DAPM context.
- */
-static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level(
-	struct snd_soc_dapm_context *dapm)
-{
-	return dapm->bias_level;
-}
-
-/**
  * snd_soc_dapm_widget_for_each_path - Iterates over all paths in the
  *   specified direction of a widget
  * @w: The widget
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 1fffef3..ddc508f 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1120,6 +1120,11 @@ static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card)
 	return card && card->instantiated;
 }
 
+static inline struct snd_soc_dapm_context *snd_soc_card_to_dapm(struct snd_soc_card *card)
+{
+	return &card->dapm;
+}
+
 /* SoC machine DAI configuration, glues a codec and cpu DAI together */
 struct snd_soc_pcm_runtime {
 	struct device *dev;
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index e85c7afd..15fac53 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -326,10 +326,14 @@ struct sof_ipc4_base_module_cfg {
 #define SOF_IPC4_MOD_INSTANCE_SHIFT		16
 #define SOF_IPC4_MOD_INSTANCE_MASK		GENMASK(23, 16)
 #define SOF_IPC4_MOD_INSTANCE(x)		((x) << SOF_IPC4_MOD_INSTANCE_SHIFT)
+#define SOF_IPC4_MOD_INSTANCE_GET(x)		(((x) & SOF_IPC4_MOD_INSTANCE_MASK) \
+						 >> SOF_IPC4_MOD_INSTANCE_SHIFT)
 
 #define SOF_IPC4_MOD_ID_SHIFT			0
 #define SOF_IPC4_MOD_ID_MASK			GENMASK(15, 0)
 #define SOF_IPC4_MOD_ID(x)			((x) << SOF_IPC4_MOD_ID_SHIFT)
+#define SOF_IPC4_MOD_ID_GET(x)			(((x) & SOF_IPC4_MOD_ID_MASK) \
+						 >> SOF_IPC4_MOD_ID_SHIFT)
 
 /* init module ipc msg */
 #define SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT	0
diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h
index c3a9efa..a21f34c 100644
--- a/include/sound/tas2781-dsp.h
+++ b/include/sound/tas2781-dsp.h
@@ -198,6 +198,14 @@ struct tasdevice_rca {
 	int ncfgs;
 	struct tasdevice_config_info **cfg_info;
 	int profile_cfg_id;
+	/*
+	 * Since version 0x105, the keyword 'init' was introduced into the
+	 * profile, which is used for chip initialization, particularly to
+	 * store common settings for other non-initialization profiles.
+	 * if (init_profile_id < 0)
+	 *         No init profile inside the RCA firmware.
+	 */
+	int init_profile_id;
 };
 
 void tasdevice_select_cfg_blk(void *context, int conf_no,
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
deleted file mode 100644
index 7a7249a..0000000
--- a/include/sound/tlv320dac33-plat.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Platform header for Texas Instruments TLV320DAC33 codec driver
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Copyright:   (C) 2009 Nokia Corporation
- */
-
-#ifndef __TLV320DAC33_PLAT_H
-#define __TLV320DAC33_PLAT_H
-
-struct tlv320dac33_platform_data {
-	int power_gpio;
-	int mode1_latency; /* latency caused by the i2c writes in us */
-	int auto_fifo_config; /* FIFO config based on the period size */
-	int keep_bclk;	/* Keep the BCLK running in FIFO modes */
-	u8 burst_bclkdiv;
-};
-
-#endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
index c9f845b..f3ff6aa 100644
--- a/include/uapi/sound/intel/avs/tokens.h
+++ b/include/uapi/sound/intel/avs/tokens.h
@@ -133,6 +133,21 @@ enum avs_tplg_token {
 	AVS_TKN_PATH_FE_FMT_ID_U32			= 1902,
 	AVS_TKN_PATH_BE_FMT_ID_U32			= 1903,
 
+	/* struct avs_tplg_path_template (conditional) */
+	AVS_TKN_CONDPATH_TMPL_ID_U32			= 1801,
+	AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING	= 2002,
+	AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32	= 2003,
+	AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING	= 2004,
+	AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32	= 2005,
+	AVS_TKN_CONDPATH_TMPL_COND_TYPE_U32		= 2006,
+	AVS_TKN_CONDPATH_TMPL_OVERRIDABLE_BOOL		= 2007,
+	AVS_TKN_CONDPATH_TMPL_PRIORITY_U8		= 2008,
+
+	/* struct avs_tplg_path (conditional) */
+	AVS_TKN_CONDPATH_ID_U32				= 1901,
+	AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32		= 2102,
+	AVS_TKN_CONDPATH_SINK_PATH_ID_U32		= 2103,
+
 	/* struct avs_tplg_pin_format */
 	AVS_TKN_PIN_FMT_INDEX_U32			= 2201,
 	AVS_TKN_PIN_FMT_IOBS_U32			= 2202,
diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h
index b9b9093..6b8102e 100644
--- a/include/uapi/sound/snd_ar_tokens.h
+++ b/include/uapi/sound/snd_ar_tokens.h
@@ -3,6 +3,8 @@
 #ifndef __SND_AR_TOKENS_H__
 #define __SND_AR_TOKENS_H__
 
+#include <linux/types.h>
+
 #define APM_SUB_GRAPH_PERF_MODE_LOW_POWER	0x1
 #define APM_SUB_GRAPH_PERF_MODE_LOW_LATENCY	0x2
 
@@ -118,6 +120,12 @@ enum ar_event_types {
  *						LPAIF_WSA = 2,
  *						LPAIF_VA = 3,
  *						LPAIF_AXI = 4
+ * Possible values for MI2S
+ *						I2S_INTF_TYPE_PRIMARY = 0,
+ *						I2S_INTF_TYPE_SECONDARY = 1,
+ *						I2S_INTF_TYPE_TERTIARY = 2,
+ *						I2S_INTF_TYPE_QUATERNARY = 3,
+ *						I2S_INTF_TYPE_QUINARY = 4,
  *
  * %AR_TKN_U32_MODULE_FMT_INTERLEAVE:		PCM Interleaving
  *						PCM_INTERLEAVED = 1,
@@ -184,8 +192,8 @@ enum ar_event_types {
 #define AR_TKN_U32_MODULE_INSTANCE_ID		201
 #define AR_TKN_U32_MODULE_MAX_IP_PORTS		202
 #define AR_TKN_U32_MODULE_MAX_OP_PORTS		203
-#define AR_TKN_U32_MODULE_IN_PORTS		204
-#define AR_TKN_U32_MODULE_OUT_PORTS		205
+#define AR_TKN_U32_MODULE_IN_PORTS		204 /* deprecated */
+#define AR_TKN_U32_MODULE_OUT_PORTS		205 /* deprecated */
 #define AR_TKN_U32_MODULE_SRC_OP_PORT_ID	206
 #define AR_TKN_U32_MODULE_DST_IN_PORT_ID	207
 #define AR_TKN_U32_MODULE_SRC_INSTANCE_ID	208
@@ -232,4 +240,12 @@ enum ar_event_types {
 #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID	260
 #define AR_TKN_U32_MODULE_LOG_MODE		261
 
+#define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006
+struct audioreach_module_priv_data {
+	__le32 size;	/* size in bytes of the array, including all elements */
+	__le32 type;	/* SND_SOC_AR_TPLG_MODULE_CFG_TYPE */
+	__le32 priv[2];	/* Private data for future expansion */
+	__le32 data[0];	/* config data */
+};
+
 #endif /* __SND_AR_TOKENS_H__ */
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index c28c766..9ce72fb 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -106,6 +106,8 @@
  */
 #define SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME	417
 
+#define SOF_TKN_COMP_SCHED_DOMAIN		418
+
 /* SSP */
 #define SOF_TKN_INTEL_SSP_CLKS_CONTROL		500
 #define SOF_TKN_INTEL_SSP_MCLK_ID		501
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index 7204096..f0c1750 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -111,6 +111,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		slave_config->dst_addr = dma_data->addr;
 		slave_config->dst_maxburst = dma_data->maxburst;
+		slave_config->dst_port_window_size = dma_data->port_window_size;
 		if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
 			slave_config->dst_addr_width =
 				DMA_SLAVE_BUSWIDTH_UNDEFINED;
@@ -119,6 +120,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
 	} else {
 		slave_config->src_addr = dma_data->addr;
 		slave_config->src_maxburst = dma_data->maxburst;
+		slave_config->src_port_window_size = dma_data->port_window_size;
 		if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
 			slave_config->src_addr_width =
 				DMA_SLAVE_BUSWIDTH_UNDEFINED;
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
index aeffd24..7e9c074 100644
--- a/sound/soc/amd/acp/acp-rembrandt.c
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -147,7 +147,7 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct acp_chip_info *chip;
-	u32 ret;
+	int ret;
 
 	chip = dev_get_platdata(&pdev->dev);
 	if (!chip || !chip->base) {
diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c
index 238b584..0160b0d 100644
--- a/sound/soc/amd/acp/amd-sdw-acpi.c
+++ b/sound/soc/amd/acp/amd-sdw-acpi.c
@@ -17,8 +17,8 @@
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/export.h>
-#include <linux/fwnode.h>
 #include <linux/module.h>
+#include <linux/property.h>
 #include <linux/soundwire/sdw_amd.h>
 #include <linux/string.h>
 
diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c
index e7f2a05..352485d 100644
--- a/sound/soc/amd/raven/acp3x-i2s.c
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -149,8 +149,9 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
 				int cmd, struct snd_soc_dai *dai)
 {
 	struct i2s_stream_instance *rtd;
-	u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+	u32 val, period_bytes, reg_val, ier_val, water_val;
 	u32 buf_size, buf_reg;
+	int ret;
 
 	rtd = substream->runtime->private_data;
 	period_bytes = frames_to_bytes(substream->runtime,
diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c
index 7dbe33f..bf719f6 100644
--- a/sound/soc/amd/vangogh/acp5x-i2s.c
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -234,8 +234,9 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
 {
 	struct i2s_stream_instance *rtd;
 	struct i2s_dev_data *adata;
-	u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+	u32 val, period_bytes, reg_val, ier_val, water_val;
 	u32 buf_size, buf_reg;
+	int ret;
 
 	adata = snd_soc_dai_get_drvdata(dai);
 	rtd = substream->runtime->private_data;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3f11b2c..59616f5 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -125,6 +125,7 @@
 	imply SND_SOC_ES7134
 	imply SND_SOC_ES7241
 	imply SND_SOC_FRAMER
+	imply SND_SOC_FS210X
 	imply SND_SOC_GTM601
 	imply SND_SOC_HDAC_HDMI
 	imply SND_SOC_HDAC_HDA
@@ -177,6 +178,7 @@
 	imply SND_SOC_NAU8825
 	imply SND_SOC_HDMI_CODEC
 	imply SND_SOC_PCM1681
+	imply SND_SOC_PCM1754
 	imply SND_SOC_PCM1789_I2C
 	imply SND_SOC_PCM179X_I2C
 	imply SND_SOC_PCM179X_SPI
@@ -192,6 +194,7 @@
 	imply SND_SOC_PCM512x_SPI
 	imply SND_SOC_PCM6240
 	imply SND_SOC_PEB2466
+	imply SND_SOC_PM4125_SDW
 	imply SND_SOC_RK3308
 	imply SND_SOC_RK3328
 	imply SND_SOC_RK817
@@ -300,7 +303,6 @@
 	imply SND_SOC_LPASS_MACRO_COMMON
 	imply SND_SOC_LPASS_RX_MACRO
 	imply SND_SOC_LPASS_TX_MACRO
-	imply SND_SOC_WL1273
 	imply SND_SOC_WM0010
 	imply SND_SOC_WM1250_EV1
 	imply SND_SOC_WM2000
@@ -622,6 +624,7 @@
 config SND_SOC_AK4641
 	tristate
 	depends on I2C
+	depends on GPIOLIB_LEGACY
 
 config SND_SOC_AK4642
 	tristate "AKM AK4642 CODEC"
@@ -1232,6 +1235,21 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-soc-framer.
 
+config SND_SOC_FS_AMP_LIB
+	select CRC16
+	tristate
+
+config SND_SOC_FS210X
+	tristate 'FourSemi FS2104/5S digital audio amplifier'
+	depends on I2C
+	select GPIOLIB
+	select REGMAP_I2C
+	select SND_SOC_FS_AMP_LIB
+	help
+	  Enable support for FourSemi FS2104/5S digital audio amplifier.
+	  The FS2104/5S are Inductor-Less, Stereo, Closed-Loop,
+	  Digital Input Class-D Power Amplifiers with Enhanced Signal Processing.
+	  The amplifiers support I2C and I2S/TDM.
 
 config SND_SOC_GTM601
 	tristate 'GTM601 UMTS modem audio codec'
@@ -1426,6 +1444,10 @@
 	tristate "Texas Instruments PCM1681 CODEC"
 	depends on I2C
 
+config SND_SOC_PCM1754
+	tristate "Texas Instruments PCM1754 CODEC"
+	depends on GPIOLIB
+
 config SND_SOC_PCM1789
 	tristate
 
@@ -1542,6 +1564,23 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-soc-peb2466.
 
+config SND_SOC_PM4125
+	depends on SND_SOC_PM4125_SDW
+	tristate
+	depends on SOUNDWIRE || !SOUNDWIRE
+
+config SND_SOC_PM4125_SDW
+	tristate "PM4125 audio codec - SDW"
+	select SND_SOC_PM4125
+	select SND_SOC_WCD_MBHC
+	select REGMAP_IRQ
+	depends on SOUNDWIRE
+	select REGMAP_SOUNDWIRE
+	help
+	  The PMIC PM4125 has an in-built audio codec IC used with SoCs
+	  like QCM2290, and it is connected via soundwire and SPMI.
+	  To compile this codec driver say Y or m.
+
 config SND_SOC_RK3308
 	tristate "Rockchip RK3308 audio CODEC"
 	depends on ARM64 || COMPILE_TEST
@@ -2175,6 +2214,7 @@
 config SND_SOC_TLV320DAC33
 	tristate
 	depends on I2C
+	depends on GPIOLIB_LEGACY
 
 config SND_SOC_TLV320ADCX140
 	tristate "Texas Instruments TLV320ADCX140 CODEC family"
@@ -2229,6 +2269,7 @@
 config SND_SOC_UDA1380
 	tristate
 	depends on I2C
+	depends on GPIOLIB_LEGACY
 
 config SND_SOC_WCD_CLASSH
 	tristate
@@ -2323,9 +2364,6 @@
 	  The WCD9390/9395 is a audio codec IC Integrated in
 	  Qualcomm SoCs like SM8650.
 
-config SND_SOC_WL1273
-	tristate
-
 config SND_SOC_WM0010
 	tristate
 	depends on SPI_MASTER
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 79e32f3..438eabd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -137,6 +137,8 @@
 snd-soc-es8375-y := es8375.o
 snd-soc-es8389-y := es8389.o
 snd-soc-framer-y := framer-codec.o
+snd-soc-fs-amp-lib-y := fs-amp-lib.o
+snd-soc-fs210x-y := fs210x.o
 snd-soc-gtm601-y := gtm601.o
 snd-soc-hdac-hdmi-y := hdac_hdmi.o
 snd-soc-hdac-hda-y := hdac_hda.o
@@ -201,6 +203,7 @@
 snd-soc-ntpfw-y := ntpfw.o
 snd-soc-hdmi-codec-y := hdmi-codec.o
 snd-soc-pcm1681-y := pcm1681.o
+snd-soc-pcm1754-y := pcm1754.o
 snd-soc-pcm1789-codec-y := pcm1789.o
 snd-soc-pcm1789-i2c-y := pcm1789-i2c.o
 snd-soc-pcm179x-codec-y := pcm179x.o
@@ -222,6 +225,8 @@
 snd-soc-pcm512x-spi-y := pcm512x-spi.o
 snd-soc-pcm6240-y := pcm6240.o
 snd-soc-peb2466-y := peb2466.o
+snd-soc-pm4125-y := pm4125.o
+snd-soc-pm4125-sdw-y := pm4125-sdw.o
 snd-soc-rk3308-y := rk3308_codec.o
 snd-soc-rk3328-y := rk3328_codec.o
 snd-soc-rk817-y := rk817_codec.o
@@ -349,7 +354,6 @@
 snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
 snd-soc-wcd939x-y := wcd939x.o
 snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
-snd-soc-wl1273-y := wl1273.o
 snd-soc-wm-adsp-y := wm_adsp.o
 snd-soc-wm0010-y := wm0010.o
 snd-soc-wm1250-ev1-y := wm1250-ev1.o
@@ -563,6 +567,8 @@
 obj-$(CONFIG_SND_SOC_ES8375)    += snd-soc-es8375.o
 obj-$(CONFIG_SND_SOC_ES8389)    += snd-soc-es8389.o
 obj-$(CONFIG_SND_SOC_FRAMER)	+= snd-soc-framer.o
+obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o
+obj-$(CONFIG_SND_SOC_FS210X)	+= snd-soc-fs210x.o
 obj-$(CONFIG_SND_SOC_GTM601)    += snd-soc-gtm601.o
 obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
 obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
@@ -622,6 +628,7 @@
 obj-$(CONFIG_SND_SOC_NTPFW)	+= snd-soc-ntpfw.o
 obj-$(CONFIG_SND_SOC_HDMI_CODEC)	+= snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)	+= snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1754)	+= snd-soc-pcm1754.o
 obj-$(CONFIG_SND_SOC_PCM179X)	+= snd-soc-pcm179x-codec.o
 obj-$(CONFIG_SND_SOC_PCM1789_I2C)	+= snd-soc-pcm1789-i2c.o
 obj-$(CONFIG_SND_SOC_PCM1789)	+= snd-soc-pcm1789-codec.o
@@ -643,6 +650,12 @@
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)	+= snd-soc-pcm512x-spi.o
 obj-$(CONFIG_SND_SOC_PCM6240)	+= snd-soc-pcm6240.o
 obj-$(CONFIG_SND_SOC_PEB2466)	+= snd-soc-peb2466.o
+obj-$(CONFIG_SND_SOC_PM4125_SDW) += snd-soc-pm4125-sdw.o
+obj-$(CONFIG_SND_SOC_PM4125)   += snd-soc-pm4125.o
+ifdef CONFIG_SND_SOC_PM4125_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125-sdw.o
+endif
 obj-$(CONFIG_SND_SOC_RK3308)	+= snd-soc-rk3308.o
 obj-$(CONFIG_SND_SOC_RK3328)	+= snd-soc-rk3328.o
 obj-$(CONFIG_SND_SOC_RK817)	+= snd-soc-rk817.o
@@ -781,7 +794,6 @@
 # avoid link failure by forcing sdw code built-in when needed
 obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
 endif
-obj-$(CONFIG_SND_SOC_WL1273)	+= snd-soc-wl1273.o
 obj-$(CONFIG_SND_SOC_WM0010)	+= snd-soc-wm0010.o
 obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
 obj-$(CONFIG_SND_SOC_WM2000)	+= snd-soc-wm2000.o
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index ae59efb..c193a9f 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -795,7 +795,7 @@ static int adau1977_set_sysclk(struct snd_soc_component *component,
 	struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
 	unsigned int mask = 0;
 	unsigned int clk_src;
-	unsigned int ret;
+	int ret;
 
 	if (dir != SND_SOC_CLOCK_IN)
 		return -EINVAL;
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
index f536501..2fde843 100644
--- a/sound/soc/codecs/cs-amp-lib-test.c
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -19,6 +19,14 @@
 #include <linux/random.h>
 #include <sound/cs-amp-lib.h>
 
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+	EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+	EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
 KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
 			    struct faux_device *)
 
@@ -196,8 +204,9 @@ static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
 
-	KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name));
-	KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid));
+	if (memcmp(name, expected_name, sizeof(expected_name)) ||
+	    efi_guidcmp(*guid, expected_guid))
+		return -EFI_NOT_FOUND;
 
 	if (!buf) {
 		*size = priv->cal_blob->size;
@@ -211,6 +220,56 @@ static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
 	return EFI_SUCCESS;
 }
 
+static efi_status_t cs_amp_lib_test_get_hp_cal_efi_variable(efi_char16_t *name,
+							    efi_guid_t *guid,
+							    unsigned long *size,
+							    void *buf)
+{
+	static const efi_char16_t expected_name[] = L"SmartAmpCalibrationData";
+	static const efi_guid_t expected_guid =
+		EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93);
+	struct kunit *test = kunit_get_current_test();
+	struct cs_amp_lib_test_priv *priv = test->priv;
+
+	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+	if (memcmp(name, expected_name, sizeof(expected_name)) ||
+	    efi_guidcmp(*guid, expected_guid))
+		return -EFI_NOT_FOUND;
+
+	if (!buf) {
+		*size = priv->cal_blob->size;
+		return EFI_BUFFER_TOO_SMALL;
+	}
+
+	KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+	memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+	return EFI_SUCCESS;
+}
+
+/* Get cal data block from HP variable. */
+static void cs_amp_lib_test_get_hp_efi_cal(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct cirrus_amp_cal_data result_data;
+	int ret;
+
+	cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_hp_cal_efi_variable);
+
+	ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_MEMEQ(test, &result_data, &priv->cal_blob->data[0], sizeof(result_data));
+}
+
 /* Get cal data block for a given amp, matched by target UID. */
 static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
 {
@@ -642,6 +701,185 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
 }
 
+static void cs_amp_lib_test_spkid_lenovo_not_present(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_none);
+
+	KUNIT_EXPECT_EQ(test, -ENOENT, cs_amp_get_vendor_spkid(dev));
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d0(efi_char16_t *name,
+							       efi_guid_t *guid,
+							       unsigned long *size,
+							       void *buf)
+{
+	struct kunit *test = kunit_get_current_test();
+
+	if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+	    memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+		return EFI_NOT_FOUND;
+
+	KUNIT_ASSERT_EQ(test, *size, 1);
+	*size = 1;
+	*(u8 *)buf = 0xd0;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d1(efi_char16_t *name,
+							       efi_guid_t *guid,
+							       unsigned long *size,
+							       void *buf)
+{
+	struct kunit *test = kunit_get_current_test();
+
+	if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+	    memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+		return EFI_NOT_FOUND;
+
+	KUNIT_ASSERT_EQ(test, *size, 1);
+	*size = 1;
+	*(u8 *)buf = 0xd1;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_00(efi_char16_t *name,
+							       efi_guid_t *guid,
+							       unsigned long *size,
+							       void *buf)
+{
+	struct kunit *test = kunit_get_current_test();
+
+	KUNIT_ASSERT_EQ(test, 0, efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID));
+	KUNIT_ASSERT_EQ(test, *size, 1);
+	*size = 1;
+	*(u8 *)buf = 0;
+
+	return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d0(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_lenovo_d0);
+
+	KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d1(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_lenovo_d1);
+
+	KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_illegal(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_lenovo_00);
+
+	KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_buf_too_small(efi_char16_t *name,
+								   efi_guid_t *guid,
+								   unsigned long *size,
+								   void *buf)
+{
+	return EFI_BUFFER_TOO_SMALL;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_oversize(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_buf_too_small);
+
+	KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_30(efi_char16_t *name,
+							   efi_guid_t *guid,
+							   unsigned long *size,
+							   void *buf)
+{
+	struct kunit *test = kunit_get_current_test();
+
+	if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+	    memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+		return EFI_NOT_FOUND;
+
+	KUNIT_ASSERT_EQ(test, *size, 1);
+	*size = 1;
+	*(u8 *)buf = 0x30;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_31(efi_char16_t *name,
+							   efi_guid_t *guid,
+							   unsigned long *size,
+							   void *buf)
+{
+	struct kunit *test = kunit_get_current_test();
+
+	if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+	    memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+		return EFI_NOT_FOUND;
+
+	KUNIT_ASSERT_EQ(test, *size, 1);
+	*size = 1;
+	*(u8 *)buf = 0x31;
+
+	return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_hp_30(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_hp_30);
+
+	KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
+{
+	struct cs_amp_lib_test_priv *priv = test->priv;
+	struct device *dev = &priv->amp_dev->dev;
+
+	kunit_activate_static_stub(test,
+				   cs_amp_test_hooks->get_efi_variable,
+				   cs_amp_lib_test_get_efi_variable_hp_31);
+
+	KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
 static int cs_amp_lib_test_case_init(struct kunit *test)
 {
 	struct cs_amp_lib_test_priv *priv;
@@ -722,6 +960,7 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
+	KUNIT_CASE(cs_amp_lib_test_get_hp_efi_cal),
 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
 			 cs_amp_lib_test_get_cal_gen_params),
 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
@@ -737,6 +976,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
 	/* Tests for writing calibration data */
 	KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
 
+	/* Test cases for speaker ID */
+	KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_not_present),
+	KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d0),
+	KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d1),
+	KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_illegal),
+	KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize),
+	KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
+	KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+
 	{ } /* terminator */
 };
 
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index 808e67c..8434d51 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -16,10 +16,35 @@
 #include <linux/types.h>
 #include <sound/cs-amp-lib.h>
 
-#define CS_AMP_CAL_GUID \
+#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
+#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
 	EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
 
-#define CS_AMP_CAL_NAME	L"CirrusSmartAmpCalibrationData"
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+	EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+	EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
+#define HP_CALIBRATION_EFI_GUID \
+	EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+
+static const struct cs_amp_lib_cal_efivar {
+	efi_char16_t *name;
+	efi_guid_t *guid;
+} cs_amp_lib_cal_efivars[] = {
+	{
+		.name = HP_CALIBRATION_EFI_NAME,
+		.guid = &HP_CALIBRATION_EFI_GUID,
+	},
+	{
+		.name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
+		.guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
+	},
+};
 
 static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
 				  const struct cirrus_amp_cal_controls *controls,
@@ -115,16 +140,41 @@ static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
 	return EFI_NOT_FOUND;
 }
 
+static int cs_amp_convert_efi_status(efi_status_t status)
+{
+	switch (status) {
+	case EFI_SUCCESS:
+		return 0;
+	case EFI_NOT_FOUND:
+		return -ENOENT;
+	case EFI_BUFFER_TOO_SMALL:
+		return -EFBIG;
+	case EFI_UNSUPPORTED:
+	case EFI_ACCESS_DENIED:
+	case EFI_SECURITY_VIOLATION:
+		return -EACCES;
+	default:
+		return -EIO;
+	}
+}
+
 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
 {
 	struct cirrus_amp_efi_data *efi_data;
 	unsigned long data_size = 0;
 	u8 *data;
 	efi_status_t status;
-	int ret;
+	int i, ret;
 
-	/* Get real size of UEFI variable */
-	status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL);
+	/* Find EFI variable and get size */
+	for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
+		status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+						 cs_amp_lib_cal_efivars[i].guid,
+						 &data_size, NULL);
+		if (status == EFI_BUFFER_TOO_SMALL)
+			break;
+	}
+
 	if (status != EFI_BUFFER_TOO_SMALL)
 		return ERR_PTR(-ENOENT);
 
@@ -138,7 +188,9 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
 	if (!data)
 		return ERR_PTR(-ENOMEM);
 
-	status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data);
+	status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+					 cs_amp_lib_cal_efivars[i].guid,
+					 &data_size, data);
 	if (status != EFI_SUCCESS) {
 		ret = -EINVAL;
 		goto err;
@@ -273,6 +325,81 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_
 }
 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
 
+struct cs_amp_spkid_efi {
+	efi_char16_t *name;
+	efi_guid_t *guid;
+	u8 values[2];
+};
+
+static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
+{
+	efi_status_t status;
+	unsigned long size;
+	u8 spkid;
+	int i, ret;
+
+	size = sizeof(spkid);
+	status = cs_amp_get_efi_variable(info->name, info->guid, &size, &spkid);
+	ret = cs_amp_convert_efi_status(status);
+	if (ret < 0)
+		return ret;
+
+	if (size == 0)
+		return -ENOENT;
+
+	for (i = 0; i < ARRAY_SIZE(info->values); i++) {
+		if (info->values[i] == spkid)
+			return i;
+	}
+
+	dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
+
+	return -EINVAL;
+}
+
+static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
+	{
+		.name = LENOVO_SPEAKER_ID_EFI_NAME,
+		.guid = &LENOVO_SPEAKER_ID_EFI_GUID,
+		.values = { 0xd0, 0xd1 },
+	},
+	{
+		.name = HP_SPEAKER_ID_EFI_NAME,
+		.guid = &HP_SPEAKER_ID_EFI_GUID,
+		.values = { 0x30, 0x31 },
+	},
+};
+
+/**
+ * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
+ * @dev:	pointer to struct device
+ *
+ * Known vendor-specific methods of speaker ID are checked and if one is
+ * found its speaker ID value is returned.
+ *
+ * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
+ *	   was not found. -EACCES if the vendor-specific storage could not
+ *	   be read. Other error values indicate that the data from the
+ *	   vendor-specific storage was found but could not be understood.
+ */
+int cs_amp_get_vendor_spkid(struct device *dev)
+{
+	int i, ret;
+
+	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+	    !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+		return -ENOENT;
+
+	for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
+		ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
+		if (ret != -ENOENT)
+			return ret;
+	}
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+
 static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
 	.get_efi_variable = cs_amp_get_efi_variable,
 	.write_cal_coeff = cs_amp_write_cal_coeff,
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 850fcf3..95d018e 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -1054,7 +1054,17 @@ int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
 	u32 speaker_id;
 	int i, ret;
 
-	/* Attempt to read the speaker type from a device property first */
+	/* Check for vendor-specific speaker ID method */
+	ret = cs_amp_get_vendor_spkid(cs35l56_base->dev);
+	if (ret >= 0) {
+		dev_dbg(cs35l56_base->dev, "Vendor Speaker ID = %d\n", ret);
+		return ret;
+	} else if (ret != -ENOENT) {
+		dev_err(cs35l56_base->dev, "Error getting vendor Speaker ID: %d\n", ret);
+		return ret;
+	}
+
+	/* Attempt to read the speaker type from a device property */
 	ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id);
 	if (!ret) {
 		dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 2a0a498..867e23d 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -684,7 +684,7 @@ static int cs42l43_run_type_detect(struct cs42l43_codec *priv)
 	}
 }
 
-static void cs42l43_clear_jack(struct cs42l43_codec *priv)
+void cs42l43_clear_jack(struct cs42l43_codec *priv)
 {
 	struct cs42l43 *cs42l43 = priv->core;
 
@@ -703,8 +703,6 @@ static void cs42l43_clear_jack(struct cs42l43_codec *priv)
 	regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
 			   CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK,
 			   0x2 << CS42L43_HSDET_MODE_SHIFT);
-
-	snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
 }
 
 void cs42l43_tip_sense_work(struct work_struct *work)
@@ -753,6 +751,8 @@ void cs42l43_tip_sense_work(struct work_struct *work)
 
 		cs42l43_clear_jack(priv);
 
+		snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
 		if (cs42l43->sdw && priv->jack_present) {
 			pm_runtime_put(priv->dev);
 			priv->jack_present = false;
@@ -903,6 +903,8 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
 
 	cs42l43_clear_jack(priv);
 
+	snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
 	if (!override) {
 		queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0);
 	} else {
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
index b0c27d6..b61df09 100644
--- a/sound/soc/codecs/cs42l43.c
+++ b/sound/soc/codecs/cs42l43.c
@@ -2210,13 +2210,12 @@ static const struct cs42l43_irq cs42l43_irqs[] = {
 };
 
 static int cs42l43_request_irq(struct cs42l43_codec *priv,
-			       struct irq_domain *dom, const char * const name,
-			       unsigned int irq, irq_handler_t handler,
-			       unsigned long flags)
+			       const char * const name, unsigned int irq,
+			       irq_handler_t handler, unsigned long flags)
 {
 	int ret;
 
-	ret = irq_create_mapping(dom, irq);
+	ret = irq_create_mapping(priv->dom, irq);
 	if (ret < 0)
 		return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name);
 
@@ -2230,13 +2229,29 @@ static int cs42l43_request_irq(struct cs42l43_codec *priv,
 	return 0;
 }
 
-static int cs42l43_shutter_irq(struct cs42l43_codec *priv,
-			       struct irq_domain *dom, unsigned int shutter,
-			       const char * const open_name,
-			       const char * const close_name,
+static void cs42l43_disable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+	int ret;
+
+	ret = irq_find_mapping(priv->dom, irq);
+	if (ret > 0)
+		disable_irq(ret);
+}
+
+static void cs42l43_enable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+	int ret;
+
+	ret = irq_find_mapping(priv->dom, irq);
+	if (ret > 0)
+		enable_irq(ret);
+}
+
+static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter,
+			       const char * const open_name, unsigned int *open_irq,
+			       const char * const close_name, unsigned int *close_irq,
 			       irq_handler_t handler)
 {
-	unsigned int open_irq, close_irq;
 	int ret;
 
 	switch (shutter) {
@@ -2244,40 +2259,35 @@ static int cs42l43_shutter_irq(struct cs42l43_codec *priv,
 		dev_warn(priv->dev, "Manual shutters, notifications not available\n");
 		return 0;
 	case 0x2:
-		open_irq = CS42L43_GPIO1_RISE;
-		close_irq = CS42L43_GPIO1_FALL;
+		*open_irq = CS42L43_GPIO1_RISE;
+		*close_irq = CS42L43_GPIO1_FALL;
 		break;
 	case 0x4:
-		open_irq = CS42L43_GPIO2_RISE;
-		close_irq = CS42L43_GPIO2_FALL;
+		*open_irq = CS42L43_GPIO2_RISE;
+		*close_irq = CS42L43_GPIO2_FALL;
 		break;
 	case 0x8:
-		open_irq = CS42L43_GPIO3_RISE;
-		close_irq = CS42L43_GPIO3_FALL;
+		*open_irq = CS42L43_GPIO3_RISE;
+		*close_irq = CS42L43_GPIO3_FALL;
 		break;
 	default:
 		return 0;
 	}
 
-	ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler, IRQF_SHARED);
+	ret = cs42l43_request_irq(priv, close_name, *close_irq, handler, IRQF_SHARED);
 	if (ret)
 		return ret;
 
-	return cs42l43_request_irq(priv, dom, open_name, open_irq, handler, IRQF_SHARED);
+	return cs42l43_request_irq(priv, open_name, *open_irq, handler, IRQF_SHARED);
 }
 
 static int cs42l43_codec_probe(struct platform_device *pdev)
 {
 	struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
 	struct cs42l43_codec *priv;
-	struct irq_domain *dom;
 	unsigned int val;
 	int i, ret;
 
-	dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
-	if (!dom)
-		return -EPROBE_DEFER;
-
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
@@ -2285,6 +2295,10 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
 	priv->dev = &pdev->dev;
 	priv->core = cs42l43;
 
+	priv->dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
+	if (!priv->dom)
+		return -EPROBE_DEFER;
+
 	platform_set_drvdata(pdev, priv);
 
 	mutex_init(&priv->jack_lock);
@@ -2314,7 +2328,7 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
 		goto err_pm;
 
 	for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
-		ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name,
+		ret = cs42l43_request_irq(priv, cs42l43_irqs[i].name,
 					  cs42l43_irqs[i].irq,
 					  cs42l43_irqs[i].handler, 0);
 		if (ret)
@@ -2327,15 +2341,17 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
 		goto err_pm;
 	}
 
-	ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK,
-				  "mic shutter open", "mic shutter close",
+	ret = cs42l43_shutter_irq(priv, val & CS42L43_MIC_SHUTTER_CFG_MASK,
+				  "mic shutter open", &priv->shutter_irqs[0],
+				  "mic shutter close", &priv->shutter_irqs[1],
 				  cs42l43_mic_shutter);
 	if (ret)
 		goto err_pm;
 
-	ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
+	ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
 				  CS42L43_SPK_SHUTTER_CFG_SHIFT,
-				  "spk shutter open", "spk shutter close",
+				  "spk shutter open", &priv->shutter_irqs[2],
+				  "spk shutter close", &priv->shutter_irqs[3],
 				  cs42l43_spk_shutter);
 	if (ret)
 		goto err_pm;
@@ -2386,22 +2402,53 @@ static int cs42l43_codec_runtime_resume(struct device *dev)
 	return 0;
 }
 
-static int cs42l43_codec_runtime_force_suspend(struct device *dev)
+static int cs42l43_codec_suspend(struct device *dev)
 {
 	struct cs42l43_codec *priv = dev_get_drvdata(dev);
+	int i;
 
-	dev_dbg(priv->dev, "Runtime suspend\n");
+	dev_dbg(priv->dev, "System suspend\n");
 
 	priv->suspend_jack_debounce = true;
 
-	pm_runtime_force_suspend(dev);
+	for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+		cs42l43_disable_irq(priv, cs42l43_irqs[i].irq);
+
+	for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+		if (priv->shutter_irqs[i])
+			cs42l43_disable_irq(priv, priv->shutter_irqs[i]);
+
+	cancel_delayed_work_sync(&priv->bias_sense_timeout);
+	cancel_delayed_work_sync(&priv->tip_sense_work);
+	cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+	cs42l43_clear_jack(priv);
+
+	return pm_runtime_force_suspend(dev);
+}
+
+static int cs42l43_codec_resume(struct device *dev)
+{
+	struct cs42l43_codec *priv = dev_get_drvdata(dev);
+	int ret, i;
+
+	ret = pm_runtime_force_resume(dev);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+		cs42l43_enable_irq(priv, cs42l43_irqs[i].irq);
+
+	for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+		if (priv->shutter_irqs[i])
+			cs42l43_enable_irq(priv, priv->shutter_irqs[i]);
 
 	return 0;
 }
 
 static const struct dev_pm_ops cs42l43_codec_pm_ops = {
 	RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
-	SYSTEM_SLEEP_PM_OPS(cs42l43_codec_runtime_force_suspend, pm_runtime_force_resume)
+	SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
 };
 
 static const struct platform_device_id cs42l43_codec_id_table[] = {
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
index 3ea3636..b2fa2cd 100644
--- a/sound/soc/codecs/cs42l43.h
+++ b/sound/soc/codecs/cs42l43.h
@@ -44,6 +44,8 @@ struct cs42l43_codec {
 	struct device *dev;
 	struct cs42l43 *core;
 	struct snd_soc_component *component;
+	struct irq_domain *dom;
+	unsigned int shutter_irqs[4];
 
 	struct clk *mclk;
 
@@ -130,6 +132,7 @@ static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream
 int cs42l43_set_jack(struct snd_soc_component *component,
 		     struct snd_soc_jack *jack, void *d);
 void cs42l43_bias_sense_timeout(struct work_struct *work);
+void cs42l43_clear_jack(struct cs42l43_codec *priv);
 void cs42l43_tip_sense_work(struct work_struct *work);
 irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data);
 irqreturn_t cs42l43_button_press(int irq, void *data);
diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c
index 59eaa9a..8ff3652 100644
--- a/sound/soc/codecs/cs48l32-tables.c
+++ b/sound/soc/codecs/cs48l32-tables.c
@@ -533,8 +533,6 @@ static const struct regmap_config cs48l32_regmap = {
 int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32)
 {
 	cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap);
-	if (IS_ERR(cs48l32->regmap))
-		return PTR_ERR(cs48l32->regmap);
 
-	return 0;
+	return PTR_ERR_OR_ZERO(cs48l32->regmap);
 }
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index a4496cc..ae89260c 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -2247,10 +2247,8 @@ static int da7213_runtime_resume(struct device *dev)
 	return regcache_sync(da7213->regmap);
 }
 
-static const struct dev_pm_ops da7213_pm = {
-	RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL)
-	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(da7213_pm, da7213_runtime_suspend,
+				 da7213_runtime_resume, NULL);
 
 static const struct i2c_device_id da7213_i2c_id[] = {
 	{ "da7213" },
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
index a982299..eb85b71 100644
--- a/sound/soc/codecs/es8323.c
+++ b/sound/soc/codecs/es8323.c
@@ -182,13 +182,13 @@ static const struct snd_kcontrol_new es8323_mono_adc_mux_controls =
 
 /* Left Mixer */
 static const struct snd_kcontrol_new es8323_left_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Left Playback Switch", SND_SOC_NOPM, 7, 1, 1),
+	SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0),
 	SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0),
 };
 
 /* Right Mixer */
 static const struct snd_kcontrol_new es8323_right_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Right Playback Switch", SND_SOC_NOPM, 6, 1, 1),
+	SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0),
 	SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0),
 };
 
@@ -211,8 +211,8 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
 
 	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
 	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
-	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", SND_SOC_NOPM, 6, 1),
-	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", SND_SOC_NOPM, 7, 1),
+	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
+	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
 
 	SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
 			   &es8323_left_mixer_controls[0],
@@ -223,10 +223,10 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
 
 	SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
 	SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
-	SND_SOC_DAPM_PGA("Right Out 2", SND_SOC_NOPM, 2, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("Left Out 2", SND_SOC_NOPM, 3, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("Right Out 1", SND_SOC_NOPM, 4, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("Left Out 1", SND_SOC_NOPM, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),
 
@@ -632,7 +632,6 @@ static int es8323_probe(struct snd_soc_component *component)
 
 	snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
 	snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
-	snd_soc_component_write(component, ES8323_DACCONTROL17, 0xB8);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c
new file mode 100644
index 0000000..c8f5661
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// fs-amp-lib.c --- Common library for FourSemi Audio Amplifiers
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/crc16.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fs-amp-lib.h"
+
+static int fs_get_scene_count(struct fs_amp_lib *amp_lib)
+{
+	const struct fs_fwm_table *table;
+	int count;
+
+	if (!amp_lib || !amp_lib->dev)
+		return -EINVAL;
+
+	table = amp_lib->table[FS_INDEX_SCENE];
+	if (!table)
+		return -EFAULT;
+
+	count = table->size / sizeof(struct fs_scene_index);
+	if (count < 1 || count > FS_SCENE_COUNT_MAX) {
+		dev_err(amp_lib->dev, "Invalid scene count: %d\n", count);
+		return -ERANGE;
+	}
+
+	return count;
+}
+
+static void fs_get_fwm_string(struct fs_amp_lib *amp_lib,
+			      int offset, const char **pstr)
+{
+	const struct fs_fwm_table *table;
+
+	if (!amp_lib || !amp_lib->dev || !pstr)
+		return;
+
+	table = amp_lib->table[FS_INDEX_STRING];
+	if (table && offset > 0 && offset < table->size + sizeof(*table))
+		*pstr = (char *)table + offset;
+	else
+		*pstr = NULL;
+}
+
+static void fs_get_scene_reg(struct fs_amp_lib *amp_lib,
+			     int offset, struct fs_amp_scene *scene)
+{
+	const struct fs_fwm_table *table;
+
+	if (!amp_lib || !amp_lib->dev || !scene)
+		return;
+
+	table = amp_lib->table[FS_INDEX_REG];
+	if (table && offset > 0 && offset < table->size + sizeof(*table))
+		scene->reg = (struct fs_reg_table *)((char *)table + offset);
+	else
+		scene->reg = NULL;
+}
+
+static void fs_get_scene_model(struct fs_amp_lib *amp_lib,
+			       int offset, struct fs_amp_scene *scene)
+{
+	const struct fs_fwm_table *table;
+	const char *ptr;
+
+	if (!amp_lib || !amp_lib->dev || !scene)
+		return;
+
+	table = amp_lib->table[FS_INDEX_MODEL];
+	ptr = (char *)table;
+	if (table && offset > 0 && offset < table->size + sizeof(*table))
+		scene->model = (struct fs_file_table *)(ptr + offset);
+	else
+		scene->model = NULL;
+}
+
+static void fs_get_scene_effect(struct fs_amp_lib *amp_lib,
+				int offset, struct fs_amp_scene *scene)
+{
+	const struct fs_fwm_table *table;
+	const char *ptr;
+
+	if (!amp_lib || !amp_lib->dev || !scene)
+		return;
+
+	table = amp_lib->table[FS_INDEX_EFFECT];
+	ptr = (char *)table;
+	if (table && offset > 0 && offset < table->size + sizeof(*table))
+		scene->effect = (struct fs_file_table *)(ptr + offset);
+	else
+		scene->effect = NULL;
+}
+
+static int fs_parse_scene_tables(struct fs_amp_lib *amp_lib)
+{
+	const struct fs_scene_index *scene_index;
+	const struct fs_fwm_table *table;
+	struct fs_amp_scene *scene;
+	int idx, count;
+
+	if (!amp_lib || !amp_lib->dev)
+		return -EINVAL;
+
+	count = fs_get_scene_count(amp_lib);
+	if (count <= 0)
+		return -EFAULT;
+
+	scene = devm_kcalloc(amp_lib->dev, count, sizeof(*scene), GFP_KERNEL);
+	if (!scene)
+		return -ENOMEM;
+
+	amp_lib->scene_count = count;
+	amp_lib->scene = scene;
+
+	table = amp_lib->table[FS_INDEX_SCENE];
+	scene_index = (struct fs_scene_index *)table->buf;
+
+	for (idx = 0; idx < count; idx++) {
+		fs_get_fwm_string(amp_lib, scene_index->name, &scene->name);
+		if (!scene->name)
+			scene->name = devm_kasprintf(amp_lib->dev,
+						     GFP_KERNEL, "S%d", idx);
+		dev_dbg(amp_lib->dev, "scene.%d name: %s\n", idx, scene->name);
+		fs_get_scene_reg(amp_lib, scene_index->reg, scene);
+		fs_get_scene_model(amp_lib, scene_index->model, scene);
+		fs_get_scene_effect(amp_lib, scene_index->effect, scene);
+		scene++;
+		scene_index++;
+	}
+
+	return 0;
+}
+
+static int fs_parse_all_tables(struct fs_amp_lib *amp_lib)
+{
+	const struct fs_fwm_table *table;
+	const struct fs_fwm_index *index;
+	const char *ptr;
+	int idx, count;
+	int ret;
+
+	if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+		return -EINVAL;
+
+	/* Parse all fwm tables */
+	table = (struct fs_fwm_table *)amp_lib->hdr->params;
+	index = (struct fs_fwm_index *)table->buf;
+	count = table->size / sizeof(*index);
+
+	for (idx = 0; idx < count; idx++, index++) {
+		if (index->type >= FS_INDEX_MAX)
+			return -ERANGE;
+		ptr = (char *)table + (int)index->offset;
+		amp_lib->table[index->type] = (struct fs_fwm_table *)ptr;
+	}
+
+	/* Parse all scene tables */
+	ret = fs_parse_scene_tables(amp_lib);
+	if (ret)
+		dev_err(amp_lib->dev, "Failed to parse scene: %d\n", ret);
+
+	return ret;
+}
+
+static int fs_verify_firmware(struct fs_amp_lib *amp_lib)
+{
+	const struct fs_fwm_header *hdr;
+	int crcsum;
+
+	if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+		return -EINVAL;
+
+	hdr = amp_lib->hdr;
+
+	/* Verify the crcsum code */
+	crcsum = crc16(0x0000, (const char *)&hdr->crc_size, hdr->crc_size);
+	if (crcsum != hdr->crc16) {
+		dev_err(amp_lib->dev, "Failed to checksum: %x-%x\n",
+			crcsum, hdr->crc16);
+		return -EFAULT;
+	}
+
+	/* Verify the devid(chip_type) */
+	if (amp_lib->devid != LO_U16(hdr->chip_type)) {
+		dev_err(amp_lib->dev, "DEVID dismatch: %04X#%04X\n",
+			amp_lib->devid, hdr->chip_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void fs_print_firmware_info(struct fs_amp_lib *amp_lib)
+{
+	const struct fs_fwm_header *hdr;
+	const char *pro_name = NULL;
+	const char *dev_name = NULL;
+
+	if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+		return;
+
+	hdr = amp_lib->hdr;
+
+	fs_get_fwm_string(amp_lib, hdr->project, &pro_name);
+	fs_get_fwm_string(amp_lib, hdr->device, &dev_name);
+
+	dev_info(amp_lib->dev, "Project: %s Device: %s\n",
+		 pro_name ? pro_name : "null",
+		 dev_name ? dev_name : "null");
+
+	dev_info(amp_lib->dev, "Date: %04d%02d%02d-%02d%02d\n",
+		 hdr->date.year, hdr->date.month, hdr->date.day,
+		 hdr->date.hour, hdr->date.minute);
+}
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name)
+{
+	const struct firmware *cont;
+	struct fs_fwm_header *hdr;
+	int ret;
+
+	if (!amp_lib || !amp_lib->dev || !name)
+		return -EINVAL;
+
+	ret = request_firmware(&cont, name, amp_lib->dev);
+	if (ret) {
+		dev_err(amp_lib->dev, "Failed to request %s: %d\n", name, ret);
+		return ret;
+	}
+
+	dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size);
+
+	hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL);
+	release_firmware(cont);
+	if (!hdr)
+		return -ENOMEM;
+
+	amp_lib->hdr = hdr;
+	ret = fs_verify_firmware(amp_lib);
+	if (ret) {
+		amp_lib->hdr = NULL;
+		return ret;
+	}
+
+	ret = fs_parse_all_tables(amp_lib);
+	if (ret) {
+		amp_lib->hdr = NULL;
+		return ret;
+	}
+
+	fs_print_firmware_info(amp_lib);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fs_amp_load_firmware);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FourSemi audio amplifier library");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.h b/sound/soc/codecs/fs-amp-lib.h
new file mode 100644
index 0000000..4a77c7b
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * fs-amp-lib.h --- Common library for FourSemi Audio Amplifiers
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS_AMP_LIB_H__
+#define __FS_AMP_LIB_H__
+
+#define HI_U16(a)		(((a) >> 8) & 0xFF)
+#define LO_U16(a)		((a) & 0xFF)
+#define FS_TABLE_NAME_LEN	(4)
+#define FS_SCENE_COUNT_MAX	(16)
+#define FS_CMD_DELAY_MS_MAX	(100) /* 100ms */
+
+#define FS_CMD_DELAY		(0xFF)
+#define FS_CMD_BURST		(0xFE)
+#define FS_CMD_UPDATE		(0xFD)
+
+#define FS_SOC_ENUM_EXT(xname, xhandler_info, xhandler_get, xhandler_put) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = xhandler_info, \
+	.get = xhandler_get, .put = xhandler_put \
+}
+
+enum fs_index_type {
+	FS_INDEX_INFO = 0,
+	FS_INDEX_STCOEF,
+	FS_INDEX_SCENE,
+	FS_INDEX_MODEL,
+	FS_INDEX_REG,
+	FS_INDEX_EFFECT,
+	FS_INDEX_STRING,
+	FS_INDEX_WOOFER,
+	FS_INDEX_MAX,
+};
+
+#pragma pack(push, 1)
+
+struct fs_reg_val {
+	u8 reg;
+	u16 val;
+};
+
+struct fs_reg_bits {
+	u8 cmd; /* FS_CMD_UPDATE */
+	u8 reg;
+	u16 val;
+	u16 mask;
+};
+
+struct fs_cmd_pkg {
+	union {
+		u8 cmd;
+		struct fs_reg_val regv;
+		struct fs_reg_bits regb;
+	};
+};
+
+struct fs_fwm_index {
+	/* Index type */
+	u16 type;
+	/* Offset address starting from the end of header */
+	u16 offset;
+};
+
+struct fs_fwm_table {
+	char name[FS_TABLE_NAME_LEN];
+	u16 size; /* size of buf */
+	u8 buf[];
+};
+
+struct fs_scene_index {
+	/* Offset address(scene name) in string table */
+	u16 name;
+	/* Offset address(scene reg) in register table */
+	u16 reg;
+	/* Offset address(scene model) in model table */
+	u16 model;
+	/* Offset address(scene effect) in effect table */
+	u16 effect;
+};
+
+struct fs_reg_table {
+	u16 size; /* size of buf */
+	u8 buf[];
+};
+
+struct fs_file_table {
+	u16 name;
+	u16 size; /* size of buf */
+	u8 buf[];
+};
+
+struct fs_fwm_date {
+	u32 year:12;
+	u32 month:4;
+	u32 day:5;
+	u32 hour:5;
+	u32 minute:6;
+};
+
+struct fs_fwm_header {
+	u16 version;
+	u16 project; /* Offset address(project name) in string table */
+	u16 device; /* Offset address(device name) in string table */
+	struct fs_fwm_date date;
+	u16 crc16;
+	u16 crc_size; /* Starting position for CRC checking */
+	u16 chip_type;
+	u16 addr; /* 7-bit i2c address */
+	u16 spkid;
+	u16 rsvd[6];
+	u8 params[];
+};
+
+#pragma pack(pop)
+
+struct fs_i2s_srate {
+	u32 srate; /* Sample rate */
+	u16 i2ssr; /* Value of Bit field[I2SSR] */
+};
+
+struct fs_pll_div {
+	unsigned int bclk; /* Rate of bit clock */
+	u16 pll1;
+	u16 pll2;
+	u16 pll3;
+};
+
+struct fs_amp_scene {
+	const char *name;
+	const struct fs_reg_table  *reg;
+	const struct fs_file_table *model;
+	const struct fs_file_table *effect;
+};
+
+struct fs_amp_lib {
+	const struct fs_fwm_header *hdr;
+	const struct fs_fwm_table *table[FS_INDEX_MAX];
+	struct fs_amp_scene *scene;
+	struct device *dev;
+	int scene_count;
+	u16 devid;
+};
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name);
+
+#endif // __FS_AMP_LIB_H__
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
new file mode 100644
index 0000000..e2f8571
--- /dev/null
+++ b/sound/soc/codecs/fs210x.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// fs210x.c -- Driver for the FS2104/5S Audio Amplifier
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "fs210x.h"
+#include "fs-amp-lib.h"
+
+#define FS210X_DEFAULT_FWM_NAME		"fs210x_fwm.bin"
+#define FS210X_DEFAULT_DAI_NAME		"fs210x-aif"
+#define FS2105S_DEVICE_ID		0x20 /* FS2105S */
+#define FS210X_DEVICE_ID		0x45 /* FS2104 */
+#define FS210X_REG_MAX			0xF8
+#define FS210X_INIT_SCENE		0
+#define FS210X_DEFAULT_SCENE		1
+#define FS210X_START_DELAY_MS		5
+#define FS210X_FAULT_CHECK_INTERVAL_MS	2000
+#define FS2105S_RATES			(SNDRV_PCM_RATE_32000 | \
+					 SNDRV_PCM_RATE_44100 | \
+					 SNDRV_PCM_RATE_48000 | \
+					 SNDRV_PCM_RATE_88200 | \
+					 SNDRV_PCM_RATE_96000)
+#define FS210X_RATES			(SNDRV_PCM_RATE_16000 | FS2105S_RATES)
+#define FS210X_FORMATS			(SNDRV_PCM_FMTBIT_S16_LE | \
+					 SNDRV_PCM_FMTBIT_S24_LE | \
+					 SNDRV_PCM_FMTBIT_S24_3LE | \
+					 SNDRV_PCM_FMTBIT_S32_LE)
+#define FS210X_NUM_SUPPLIES		ARRAY_SIZE(fs210x_supply_names)
+
+static const char *const fs210x_supply_names[] = {
+	"pvdd",
+	"dvdd",
+};
+
+struct fs210x_platform_data {
+	const char *fwm_name;
+};
+
+struct fs210x_priv {
+	struct i2c_client *i2c;
+	struct device *dev;
+	struct regmap *regmap;
+	struct fs210x_platform_data pdata;
+	struct regulator_bulk_data supplies[FS210X_NUM_SUPPLIES];
+	struct gpio_desc *gpio_sdz;
+	struct delayed_work start_work;
+	struct delayed_work fault_check_work;
+	struct fs_amp_lib amp_lib;
+	const struct fs_amp_scene *cur_scene;
+	struct clk *clk_bclk;
+	/*
+	 * @lock: Mutex ensuring exclusive access for critical device operations
+	 *
+	 * This lock serializes access between the following actions:
+	 *  - Device initialization procedures(probe)
+	 *  - Enable/disable device(DAPM event)
+	 *  - Suspend/resume device(PM)
+	 *  - Runtime scene switching(control)
+	 *  - Scheduling/execution of delayed works items(delayed works)
+	 */
+	struct mutex lock;
+	unsigned int check_interval_ms;
+	unsigned int bclk;
+	unsigned int srate;
+	int scene_id;
+	u16 devid;
+	bool is_inited;
+	bool is_suspended;
+	bool is_bclk_on;
+	bool is_playing;
+};
+
+static const unsigned int fs2105s_rates[] = {
+	32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs2105s_constraints = {
+	.count = ARRAY_SIZE(fs2105s_rates),
+	.list  = fs2105s_rates,
+};
+
+static const unsigned int fs210x_rates[] = {
+	16000, 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs210x_constraints = {
+	.count = ARRAY_SIZE(fs210x_rates),
+	.list  = fs210x_rates,
+};
+
+static const struct fs_pll_div fs210x_pll_div[] = {
+	/*    bclk,   pll1,   pll2,   pll3 */
+	{   512000, 0x006C, 0x0120, 0x0001 },
+	{   768000, 0x016C, 0x00C0, 0x0001 },
+	{  1024000, 0x016C, 0x0090, 0x0001 },
+	{  1536000, 0x016C, 0x0060, 0x0001 },
+	{  2048000, 0x016C, 0x0090, 0x0002 },
+	{  2304000, 0x016C, 0x0080, 0x0002 },
+	{  3072000, 0x016C, 0x0090, 0x0003 },
+	{  4096000, 0x016C, 0x0090, 0x0004 },
+	{  4608000, 0x016C, 0x0080, 0x0004 },
+	{  6144000, 0x016C, 0x0090, 0x0006 },
+	{  8192000, 0x016C, 0x0090, 0x0008 },
+	{  9216000, 0x016C, 0x0090, 0x0009 },
+	{ 12288000, 0x016C, 0x0090, 0x000C },
+	{ 16384000, 0x016C, 0x0090, 0x0010 },
+	{ 18432000, 0x016C, 0x0090, 0x0012 },
+	{ 24576000, 0x016C, 0x0090, 0x0018 },
+	{  1411200, 0x016C, 0x0060, 0x0001 },
+	{  2116800, 0x016C, 0x0080, 0x0002 },
+	{  2822400, 0x016C, 0x0090, 0x0003 },
+	{  4233600, 0x016C, 0x0080, 0x0004 },
+	{  5644800, 0x016C, 0x0090, 0x0006 },
+	{  8467200, 0x016C, 0x0090, 0x0009 },
+	{ 11289600, 0x016C, 0x0090, 0x000C },
+	{ 16934400, 0x016C, 0x0090, 0x0012 },
+	{ 22579200, 0x016C, 0x0090, 0x0018 },
+	{  2000000, 0x017C, 0x0093, 0x0002 },
+};
+
+static int fs210x_bclk_set(struct fs210x_priv *fs210x, bool on)
+{
+	int ret = 0;
+
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	if ((fs210x->is_bclk_on ^ on) == 0)
+		return 0;
+
+	if (on) {
+		clk_set_rate(fs210x->clk_bclk, fs210x->bclk);
+		ret = clk_prepare_enable(fs210x->clk_bclk);
+		fs210x->is_bclk_on = true;
+		fsleep(2000); /* >= 2ms */
+	} else {
+		clk_disable_unprepare(fs210x->clk_bclk);
+		fs210x->is_bclk_on = false;
+	}
+
+	return ret;
+}
+
+static int fs210x_reg_write(struct fs210x_priv *fs210x,
+			    u8 reg, u16 val)
+{
+	int ret;
+
+	ret = regmap_write(fs210x->regmap, reg, val);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to write %02Xh: %d\n", reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fs210x_reg_read(struct fs210x_priv *fs210x,
+			   u8 reg, u16 *pval)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(fs210x->regmap, reg, &val);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to read %02Xh: %d\n", reg, ret);
+		return ret;
+	}
+
+	*pval = (u16)val;
+
+	return 0;
+}
+
+static int fs210x_reg_update_bits(struct fs210x_priv *fs210x,
+				  u8 reg, u16 mask, u16 val)
+{
+	int ret;
+
+	ret = regmap_update_bits(fs210x->regmap, reg, mask, val);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to update %02Xh: %d\n", reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fs210x_reg_bulk_write(struct fs210x_priv *fs210x,
+				 u8 reg, const void *val, u32 size)
+{
+	int ret;
+
+	ret = regmap_bulk_write(fs210x->regmap, reg, val, size / 2);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to bulk write %02Xh: %d\n",
+			reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static inline int fs210x_write_reg_val(struct fs210x_priv *fs210x,
+				       const struct fs_reg_val *regv)
+{
+	return fs210x_reg_write(fs210x, regv->reg, regv->val);
+}
+
+static inline int fs210x_write_reg_bits(struct fs210x_priv *fs210x,
+					const struct fs_reg_bits *regu)
+{
+	return fs210x_reg_update_bits(fs210x,
+				      regu->reg,
+				      regu->mask,
+				      regu->val);
+}
+
+static inline int fs210x_set_cmd_pkg(struct fs210x_priv *fs210x,
+				     const struct fs_cmd_pkg *pkg,
+				     unsigned int *offset)
+{
+	int delay_us;
+
+	if (pkg->cmd >= 0x00 && pkg->cmd <= FS210X_REG_MAX) {
+		*offset = sizeof(pkg->regv);
+		return fs210x_write_reg_val(fs210x, &pkg->regv);
+	} else if (pkg->cmd == FS_CMD_UPDATE) {
+		*offset = sizeof(pkg->regb);
+		return fs210x_write_reg_bits(fs210x, &pkg->regb);
+	} else if (pkg->cmd == FS_CMD_DELAY) {
+		if (pkg->regv.val > FS_CMD_DELAY_MS_MAX)
+			return -EOPNOTSUPP;
+		delay_us = pkg->regv.val * 1000; /* ms -> us */
+		fsleep(delay_us);
+		*offset = sizeof(pkg->regv);
+		return 0;
+	}
+
+	dev_err(fs210x->dev, "Invalid pkg cmd: %d\n", pkg->cmd);
+
+	return -EOPNOTSUPP;
+}
+
+static int fs210x_reg_write_table(struct fs210x_priv *fs210x,
+				  const struct fs_reg_table *reg)
+{
+	const struct fs_cmd_pkg *pkg;
+	unsigned int index, offset;
+	int ret;
+
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	if (!reg || reg->size == 0)
+		return -EFAULT;
+
+	for (index = 0; index < reg->size; index += offset) {
+		pkg = (struct fs_cmd_pkg *)(reg->buf + index);
+		ret = fs210x_set_cmd_pkg(fs210x, pkg, &offset);
+		if (ret) {
+			dev_err(fs210x->dev, "Failed to set cmd pkg: %02X-%d\n",
+				pkg->cmd, ret);
+			return ret;
+		}
+	}
+
+	if (index != reg->size) {
+		dev_err(fs210x->dev, "Invalid reg table size: %d-%d\n",
+			index, reg->size);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int fs210x_dev_play(struct fs210x_priv *fs210x)
+{
+	int ret;
+
+	if (!fs210x->is_inited)
+		return -EFAULT;
+
+	if (fs210x->is_playing)
+		return 0;
+
+	ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+			       FS210X_11H_DPS_PLAY);
+	if (!ret)
+		fs210x->is_playing = true;
+
+	fsleep(10000); /* >= 10ms */
+
+	return ret;
+}
+
+static int fs210x_dev_stop(struct fs210x_priv *fs210x)
+{
+	int ret;
+
+	if (!fs210x->is_inited)
+		return -EFAULT;
+
+	if (!fs210x->is_playing)
+		return 0;
+
+	ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+			       FS210X_11H_DPS_PWDN);
+	fs210x->is_playing = false;
+
+	fsleep(30000); /* >= 30ms */
+
+	return ret;
+}
+
+static int fs210x_set_reg_table(struct fs210x_priv *fs210x,
+				const struct fs_amp_scene *scene)
+{
+	const struct fs_amp_scene *cur_scene;
+	const struct fs_reg_table *reg;
+
+	if (!fs210x || !fs210x->dev || !scene)
+		return -EINVAL;
+
+	cur_scene = fs210x->cur_scene;
+	if (!scene->reg || cur_scene == scene) {
+		dev_dbg(fs210x->dev, "Skip writing reg table\n");
+		return 0;
+	}
+
+	reg = scene->reg;
+	dev_dbg(fs210x->dev, "reg table size: %d\n", reg->size);
+
+	return fs210x_reg_write_table(fs210x, reg);
+}
+
+static int fs210x_set_woofer_table(struct fs210x_priv *fs210x)
+{
+	const struct fs_file_table *woofer;
+	const struct fs_fwm_table *table;
+	int ret;
+
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	/* NOTE: fs2105s has woofer ram only */
+	if (fs210x->devid != FS2105S_DEVICE_ID)
+		return 0;
+
+	table = fs210x->amp_lib.table[FS_INDEX_WOOFER];
+	if (!table) {
+		dev_dbg(fs210x->dev, "Skip writing woofer table\n");
+		return 0;
+	}
+
+	woofer = (struct fs_file_table *)table->buf;
+	dev_dbg(fs210x->dev, "woofer table size: %d\n", woofer->size);
+	/* Unit of woofer data is u32(4 bytes) */
+	if (woofer->size == 0 || (woofer->size & 0x3)) {
+		dev_err(fs210x->dev, "Invalid woofer size: %d\n",
+			woofer->size);
+		return -EINVAL;
+	}
+
+	ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+			       FS2105S_46H_CAM_BURST_W);
+	ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+				     woofer->buf, woofer->size);
+
+	return ret;
+}
+
+static int fs210x_set_effect_table(struct fs210x_priv *fs210x,
+				   const struct fs_amp_scene *scene)
+{
+	const struct fs_amp_scene *cur_scene;
+	const struct fs_file_table *effect;
+	int half_size;
+	int ret;
+
+	if (!fs210x || !fs210x->dev || !scene)
+		return -EINVAL;
+
+	cur_scene = fs210x->cur_scene;
+	if (!scene->effect || cur_scene == scene) {
+		dev_dbg(fs210x->dev, "Skip writing effect table\n");
+		return 0;
+	}
+
+	effect = scene->effect;
+	dev_dbg(fs210x->dev, "effect table size: %d\n", effect->size);
+
+	/* Unit of effect data is u32(4 bytes), 2 channels */
+	if (effect->size == 0 || (effect->size & 0x7)) {
+		dev_err(fs210x->dev, "Invalid effect size: %d\n",
+			effect->size);
+		return -EINVAL;
+	}
+
+	half_size = effect->size / 2;
+
+	/* Left channel */
+	ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+			       FS210X_46H_CAM_BURST_L);
+	ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+				     effect->buf, half_size);
+	if (ret)
+		return ret;
+
+	/* Right channel */
+	ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+			       FS210X_46H_CAM_BURST_R);
+	ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+				     effect->buf + half_size, half_size);
+
+	return ret;
+}
+
+static int fs210x_access_dsp_ram(struct fs210x_priv *fs210x, bool enable)
+{
+	int ret;
+
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	if (enable) {
+		ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+				       FS210X_11H_DPS_HIZ);
+		ret |= fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+					FS210X_0BH_ACCKEY_ON);
+	} else {
+		ret = fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+				       FS210X_0BH_ACCKEY_OFF);
+		ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+					FS210X_11H_DPS_PWDN);
+	}
+
+	fsleep(10000); /* >= 10ms */
+
+	return ret;
+}
+
+static int fs210x_write_dsp_effect(struct fs210x_priv *fs210x,
+				   const struct fs_amp_scene *scene,
+				   int scene_id)
+{
+	int ret;
+
+	if (!fs210x || !scene)
+		return -EINVAL;
+
+	ret = fs210x_access_dsp_ram(fs210x, true);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to access dsp: %d\n", ret);
+		goto tag_exit;
+	}
+
+	ret = fs210x_set_effect_table(fs210x, scene);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to set effect: %d\n", ret);
+		goto tag_exit;
+	}
+
+	if (scene_id == FS210X_INIT_SCENE)
+		ret = fs210x_set_woofer_table(fs210x);
+
+tag_exit:
+	fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+			 FS210X_46H_CAM_CLEAR);
+	fs210x_access_dsp_ram(fs210x, false);
+
+	return ret;
+}
+
+static int fs210x_check_scene(struct fs210x_priv *fs210x,
+			      int scene_id, bool *skip_set)
+{
+	struct fs_amp_lib *amp_lib;
+
+	if (!fs210x || !skip_set)
+		return -EINVAL;
+
+	amp_lib = &fs210x->amp_lib;
+	if (amp_lib->scene_count == 0 || !amp_lib->scene) {
+		dev_err(fs210x->dev, "There's no scene data\n");
+		return -EINVAL;
+	}
+
+	if (scene_id < 0 || scene_id >= amp_lib->scene_count) {
+		dev_err(fs210x->dev, "Invalid scene_id: %d\n", scene_id);
+		return -EINVAL;
+	}
+
+	if (fs210x->scene_id == scene_id) {
+		dev_dbg(fs210x->dev, "Skip to set same scene\n");
+		return 0;
+	}
+
+	*skip_set = false;
+
+	return 0;
+}
+
+static int fs210x_set_scene(struct fs210x_priv *fs210x, int scene_id)
+{
+	const struct fs_amp_scene *scene;
+	bool skip_set = true;
+	bool is_playing;
+	int ret;
+
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	ret = fs210x_check_scene(fs210x, scene_id, &skip_set);
+	if (ret || skip_set)
+		return ret;
+
+	scene = fs210x->amp_lib.scene + scene_id;
+	dev_info(fs210x->dev, "Switch scene.%d: %s\n",
+		 scene_id, scene->name);
+
+	is_playing = fs210x->is_playing;
+	if (is_playing)
+		fs210x_dev_stop(fs210x);
+
+	ret = fs210x_set_reg_table(fs210x, scene);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to set reg: %d\n", ret);
+		return ret;
+	}
+
+	ret = fs210x_write_dsp_effect(fs210x, scene, scene_id);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to write ram: %d\n", ret);
+		return ret;
+	}
+
+	fs210x->cur_scene = scene;
+	fs210x->scene_id  = scene_id;
+
+	if (is_playing)
+		fs210x_dev_play(fs210x);
+
+	return 0;
+}
+
+static int fs210x_init_chip(struct fs210x_priv *fs210x)
+{
+	int scene_id;
+	int ret;
+
+	regcache_cache_bypass(fs210x->regmap, true);
+
+	if (!fs210x->gpio_sdz) {
+		/* Gpio is not found, i2c reset */
+		ret = fs210x_reg_write(fs210x, FS210X_10H_PWRCTRL,
+				       FS210X_10H_I2C_RESET);
+		if (ret)
+			goto tag_power_down;
+	} else {
+		/* gpio reset, deactivate */
+		gpiod_set_value_cansleep(fs210x->gpio_sdz, 0);
+	}
+
+	fsleep(10000); /* >= 10ms */
+
+	/* Backup scene id */
+	scene_id = fs210x->scene_id;
+	fs210x->scene_id = -1;
+
+	/* Init registers/RAM by init scene */
+	ret = fs210x_set_scene(fs210x, FS210X_INIT_SCENE);
+	if (ret)
+		goto tag_power_down;
+
+	/*
+	 * If the firmware has effect scene(s),
+	 * we load effect scene by default scene or scene_id
+	 */
+	if (fs210x->amp_lib.scene_count > 1) {
+		if (scene_id < FS210X_DEFAULT_SCENE)
+			scene_id = FS210X_DEFAULT_SCENE;
+		ret = fs210x_set_scene(fs210x, scene_id);
+		if (ret)
+			goto tag_power_down;
+	}
+
+tag_power_down:
+	/* Power down the device */
+	ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+				FS210X_11H_DPS_PWDN);
+	fsleep(10000); /* >= 10ms */
+
+	regcache_cache_bypass(fs210x->regmap, false);
+	if (!ret) {
+		regcache_mark_dirty(fs210x->regmap);
+		regcache_sync(fs210x->regmap);
+		fs210x->is_inited = true;
+	}
+
+	return ret;
+}
+
+static int fs210x_set_i2s_params(struct fs210x_priv *fs210x)
+{
+	const struct fs_i2s_srate params[] = {
+		{ 16000, 0x3 },
+		{ 32000, 0x7 },
+		{ 44100, 0x8 },
+		{ 48000, 0x9 },
+		{ 88200, 0xA },
+		{ 96000, 0xB },
+	};
+	u16 val;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(params); i++) {
+		if (params[i].srate != fs210x->srate)
+			continue;
+		val = params[i].i2ssr << FS210X_17H_I2SSR_SHIFT;
+		ret = fs210x_reg_update_bits(fs210x,
+					     FS210X_17H_I2SCTRL,
+					     FS210X_17H_I2SSR_MASK,
+					     val);
+		return ret;
+	}
+
+	dev_err(fs210x->dev, "Invalid sample rate: %d\n", fs210x->srate);
+
+	return -EINVAL;
+}
+
+static int fs210x_get_pll_div(struct fs210x_priv *fs210x,
+			      const struct fs_pll_div **pll_div)
+{
+	int i;
+
+	if (!fs210x || !pll_div)
+		return -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(fs210x_pll_div); i++) {
+		if (fs210x_pll_div[i].bclk != fs210x->bclk)
+			continue;
+		*pll_div = fs210x_pll_div + i;
+		return 0;
+	}
+
+	dev_err(fs210x->dev, "No PLL table for bclk: %d\n", fs210x->bclk);
+
+	return -EFAULT;
+}
+
+static int fs210x_set_hw_params(struct fs210x_priv *fs210x)
+{
+	const struct fs_pll_div *pll_div;
+	int ret;
+
+	ret = fs210x_set_i2s_params(fs210x);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to set i2s params: %d\n", ret);
+		return ret;
+	}
+
+	/* Set pll params */
+	ret = fs210x_get_pll_div(fs210x, &pll_div);
+	if (ret)
+		return ret;
+
+	ret  = fs210x_reg_write(fs210x, FS210X_A1H_PLLCTRL1, pll_div->pll1);
+	ret |= fs210x_reg_write(fs210x, FS210X_A2H_PLLCTRL2, pll_div->pll2);
+	ret |= fs210x_reg_write(fs210x, FS210X_A3H_PLLCTRL3, pll_div->pll3);
+
+	return ret;
+}
+
+static int fs210x_dai_startup(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	const struct snd_pcm_hw_constraint_list *list;
+	struct fs210x_priv *fs210x;
+	int ret;
+
+	fs210x = snd_soc_component_get_drvdata(dai->component);
+	if (!fs210x) {
+		pr_err("dai_startup: fs210x is null\n");
+		return -EINVAL;
+	}
+
+	if (!substream->runtime)
+		return 0;
+
+	ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+					   SNDRV_PCM_HW_PARAM_FORMAT,
+					   FS210X_FORMATS);
+	if (ret < 0) {
+		dev_err(fs210x->dev,
+			"Failed to set hw param format: %d\n", ret);
+		return ret;
+	}
+
+	if (fs210x->devid == FS2105S_DEVICE_ID)
+		list = &fs2105s_constraints;
+	else
+		list = &fs210x_constraints;
+
+	ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_RATE,
+					 list);
+	if (ret < 0) {
+		dev_err(fs210x->dev,
+			"Failed to set hw param rate: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fs210x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct fs210x_priv *fs210x;
+
+	fs210x = snd_soc_component_get_drvdata(dai->component);
+
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+	case SND_SOC_DAIFMT_CBC_CFC:
+		/* Only supports consumer mode */
+		break;
+	default:
+		dev_err(fs210x->dev, "Only supports consumer mode\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fs210x_dai_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct fs210x_priv *fs210x;
+	int chn_num;
+	int ret;
+
+	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+		return 0;
+
+	fs210x = snd_soc_component_get_drvdata(dai->component);
+
+	fs210x->srate = params_rate(params);
+	fs210x->bclk  = snd_soc_params_to_bclk(params);
+	chn_num = params_channels(params);
+	if (chn_num == 1) /* mono */
+		fs210x->bclk *= 2; /* I2S bus has 2 channels */
+
+	/* The FS2105S can't support 16kHz sample rate. */
+	if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000)
+		return -EOPNOTSUPP;
+
+	mutex_lock(&fs210x->lock);
+	ret = fs210x_set_hw_params(fs210x);
+	mutex_unlock(&fs210x->lock);
+	if (ret)
+		dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret);
+
+	return ret;
+}
+
+static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct fs210x_priv *fs210x;
+	unsigned long delay;
+
+	if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+		return 0;
+
+	fs210x = snd_soc_component_get_drvdata(dai->component);
+
+	mutex_lock(&fs210x->lock);
+
+	if (!fs210x->is_inited || fs210x->is_suspended) {
+		mutex_unlock(&fs210x->lock);
+		return 0;
+	}
+
+	mutex_unlock(&fs210x->lock);
+
+	if (mute) {
+		cancel_delayed_work_sync(&fs210x->fault_check_work);
+		cancel_delayed_work_sync(&fs210x->start_work);
+	} else {
+		delay = msecs_to_jiffies(fs210x->check_interval_ms);
+		schedule_delayed_work(&fs210x->fault_check_work, delay);
+	}
+
+	return 0;
+}
+
+static int fs210x_dai_trigger(struct snd_pcm_substream *substream,
+			      int cmd, struct snd_soc_dai *dai)
+{
+	struct fs210x_priv *fs210x;
+
+	fs210x = snd_soc_component_get_drvdata(dai->component);
+
+	mutex_lock(&fs210x->lock);
+
+	if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) {
+		mutex_unlock(&fs210x->lock);
+		return 0;
+	}
+
+	mutex_unlock(&fs210x->lock);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/*
+		 * According to the power up/down sequence of FS210x,
+		 * it requests the I2S clock has been present
+		 * and stable(>= 2ms) before playing.
+		 */
+		schedule_delayed_work(&fs210x->start_work,
+				      msecs_to_jiffies(FS210X_START_DELAY_MS));
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void fs210x_start_work(struct work_struct *work)
+{
+	struct fs210x_priv *fs210x;
+	int ret;
+
+	fs210x = container_of(work, struct fs210x_priv, start_work.work);
+
+	mutex_lock(&fs210x->lock);
+
+	ret = fs210x_dev_play(fs210x);
+	if (ret)
+		dev_err(fs210x->dev, "Failed to start playing: %d\n", ret);
+
+	mutex_unlock(&fs210x->lock);
+}
+
+static void fs210x_fault_check_work(struct work_struct *work)
+{
+	struct fs210x_priv *fs210x;
+	u16 status;
+	int ret;
+
+	fs210x = container_of(work, struct fs210x_priv, fault_check_work.work);
+
+	mutex_lock(&fs210x->lock);
+
+	if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) {
+		mutex_unlock(&fs210x->lock);
+		return;
+	}
+
+	ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status);
+	mutex_unlock(&fs210x->lock);
+	if (ret)
+		return;
+
+	if (!(status & FS210X_05H_PVDD_MASK))
+		dev_err(fs210x->dev, "PVDD fault\n");
+	if (status & FS210X_05H_OCDL_MASK)
+		dev_err(fs210x->dev, "OC detected\n");
+	if (status & FS210X_05H_UVDL_MASK)
+		dev_err(fs210x->dev, "UV detected\n");
+	if (status & FS210X_05H_OVDL_MASK)
+		dev_err(fs210x->dev, "OV detected\n");
+	if (status & FS210X_05H_OTPDL_MASK)
+		dev_err(fs210x->dev, "OT detected\n");
+	if (status & FS210X_05H_OCRDL_MASK)
+		dev_err(fs210x->dev, "OCR detected\n");
+	if (status & FS210X_05H_OCLDL_MASK)
+		dev_err(fs210x->dev, "OCL detected\n");
+	if (status & FS210X_05H_DCRDL_MASK)
+		dev_err(fs210x->dev, "DCR detected\n");
+	if (status & FS210X_05H_DCLDL_MASK)
+		dev_err(fs210x->dev, "DCL detected\n");
+	if (status & FS210X_05H_SRDL_MASK)
+		dev_err(fs210x->dev, "SR detected\n");
+	if (status & FS210X_05H_OTWDL_MASK)
+		dev_err(fs210x->dev, "OTW detected\n");
+	if (!(status & FS210X_05H_AMPS_MASK))
+		dev_dbg(fs210x->dev, "Amplifier unready\n");
+	if (!(status & FS210X_05H_PLLS_MASK))
+		dev_err(fs210x->dev, "PLL unlock\n");
+	if (!(status & FS210X_05H_ANAS_MASK))
+		dev_err(fs210x->dev, "Analog power fault\n");
+
+	schedule_delayed_work(&fs210x->fault_check_work,
+			      msecs_to_jiffies(fs210x->check_interval_ms));
+}
+
+static int fs210x_get_drvdata_from_kctrl(struct snd_kcontrol *kctrl,
+					 struct fs210x_priv **fs210x)
+{
+	struct snd_soc_component *cmpnt;
+
+	if (!kctrl) {
+		pr_err("fs210x: kcontrol is null\n");
+		return -EINVAL;
+	}
+
+	cmpnt = snd_soc_kcontrol_component(kctrl);
+	if (!cmpnt) {
+		pr_err("fs210x: component is null\n");
+		return -EINVAL;
+	}
+
+	*fs210x = snd_soc_component_get_drvdata(cmpnt);
+
+	return 0;
+}
+
+static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	const struct fs_amp_scene *scene;
+	struct fs210x_priv *fs210x;
+	const char *name = "N/A";
+	int idx, count;
+	int ret;
+
+	ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+	if (ret || !fs210x->dev) {
+		pr_err("scene_effect_info: fs210x is null\n");
+		return -EINVAL;
+	}
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+
+	count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+	if (count < 1) {
+		uinfo->value.enumerated.items = 0;
+		return 0;
+	}
+
+	uinfo->value.enumerated.items = count;
+	if (uinfo->value.enumerated.item >= count)
+		uinfo->value.enumerated.item = count - 1;
+
+	idx = uinfo->value.enumerated.item;
+	scene = fs210x->amp_lib.scene + idx + 1;
+	if (scene->name)
+		name = scene->name;
+
+	strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1);
+
+	return 0;
+}
+
+static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct fs210x_priv *fs210x;
+	int index;
+	int ret;
+
+	ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+	if (ret || !fs210x->dev) {
+		pr_err("scene_effect_get: fs210x is null\n");
+		return -EINVAL;
+	}
+
+	/* The id of effect scene is from 1 to N. */
+	if (fs210x->scene_id < 1)
+		return -EINVAL;
+
+	mutex_lock(&fs210x->lock);
+	/*
+	 * FS210x has scene(s) as below:
+	 * init scene: id = 0
+	 * effect scene(s): id = 1~N (optional)
+	 * effect_index = scene_id - 1
+	 */
+	index = fs210x->scene_id - 1;
+	ucontrol->value.integer.value[0] = index;
+	mutex_unlock(&fs210x->lock);
+
+	return 0;
+}
+
+static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct fs210x_priv *fs210x;
+	int scene_id, scene_count;
+	bool is_changed = false;
+	int ret;
+
+	ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+	if (ret || !fs210x->dev) {
+		pr_err("scene_effect_put: fs210x is null\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&fs210x->lock);
+
+	/*
+	 * FS210x has scene(s) as below:
+	 * init scene: id = 0 (It's set in fs210x_init_chip() only)
+	 * effect scene(s): id = 1~N (optional)
+	 * scene_id = effect_index + 1.
+	 */
+	scene_id = ucontrol->value.integer.value[0] + 1;
+	scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+	if (scene_id < 1 || scene_id > scene_count) {
+		mutex_unlock(&fs210x->lock);
+		return -ERANGE;
+	}
+
+	if (scene_id != fs210x->scene_id)
+		is_changed = true;
+
+	if (fs210x->is_suspended) {
+		fs210x->scene_id = scene_id;
+		mutex_unlock(&fs210x->lock);
+		return is_changed;
+	}
+
+	ret = fs210x_set_scene(fs210x, scene_id);
+	if (ret)
+		dev_err(fs210x->dev, "Failed to set scene: %d\n", ret);
+
+	mutex_unlock(&fs210x->lock);
+
+	if (!ret && is_changed)
+		return 1;
+
+	return ret;
+}
+
+static int fs210x_playback_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kc, int event)
+{
+	struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+	struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt);
+	int ret = 0;
+
+	mutex_lock(&fs210x->lock);
+
+	if (fs210x->is_suspended) {
+		mutex_unlock(&fs210x->lock);
+		return 0;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/*
+		 * If there is no bclk for us to set the clock output,
+		 * we will enable the device(start_work) in dai trigger.
+		 */
+		if (!fs210x->clk_bclk)
+			break;
+		fs210x_bclk_set(fs210x, true);
+		ret = fs210x_dev_play(fs210x);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = fs210x_dev_stop(fs210x);
+		fs210x_bclk_set(fs210x, false);
+		break;
+	default:
+		break;
+	}
+
+	mutex_unlock(&fs210x->lock);
+
+	return ret;
+}
+
+static const struct snd_soc_dai_ops fs210x_dai_ops = {
+	.startup		= fs210x_dai_startup,
+	.set_fmt		= fs210x_dai_set_fmt,
+	.hw_params		= fs210x_dai_hw_params,
+	.mute_stream		= fs210x_dai_mute,
+	.trigger		= fs210x_dai_trigger,
+};
+
+static const struct snd_soc_dai_driver fs210x_dai = {
+	.name = FS210X_DEFAULT_DAI_NAME,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = FS210X_RATES,
+		.formats = FS210X_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = FS210X_RATES,
+		.formats = FS210X_FORMATS,
+	},
+	.ops = &fs210x_dai_ops,
+	.symmetric_rate = 1,
+	.symmetric_sample_bits = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(fs2105s_vol_tlv, -9709, 19, 1);
+static const DECLARE_TLV_DB_SCALE(fs210x_vol_tlv, -13357, 19, 1);
+
+static const struct snd_kcontrol_new fs2105s_vol_control[] = {
+	SOC_DOUBLE_R_TLV("PCM Playback Volume",
+			 FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+			 7, 0x1FF, 0, fs2105s_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_vol_control[] = {
+	SOC_DOUBLE_R_TLV("PCM Playback Volume",
+			 FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+			 6, 0x2BF, 0, fs210x_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_controls[] = {
+	SOC_DOUBLE("DAC Mute Switch", FS210X_30H_DACCTRL, 4, 8, 1, 0),
+	SOC_DOUBLE("DAC Fade Switch", FS210X_30H_DACCTRL, 5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new fs210x_scene_control[] = {
+	FS_SOC_ENUM_EXT("Effect Scene",
+			fs210x_effect_scene_info,
+			fs210x_effect_scene_get,
+			fs210x_effect_scene_put),
+};
+
+static const struct snd_soc_dapm_widget fs210x_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN_E("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0,
+			      fs210x_playback_event,
+			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_OUTPUT("OUTL"),
+	SND_SOC_DAPM_OUTPUT("OUTR"),
+	SND_SOC_DAPM_INPUT("SDO"),
+};
+
+static const struct snd_soc_dapm_route fs210x_dapm_routes[] = {
+	{ "OUTL", NULL, "AIF IN" },
+	{ "OUTR", NULL, "AIF IN" },
+	{ "AIF OUT", NULL, "SDO" },
+};
+
+static int fs210x_add_mixer_controls(struct fs210x_priv *fs210x,
+				     struct snd_soc_component *cmpnt)
+{
+	const struct snd_kcontrol_new *kctrl;
+	int count;
+	int ret;
+
+	if (!fs210x || !cmpnt)
+		return -EINVAL;
+
+	if (fs210x->devid == FS2105S_DEVICE_ID) {
+		kctrl = fs2105s_vol_control;
+		count = ARRAY_SIZE(fs2105s_vol_control);
+	} else {
+		kctrl = fs210x_vol_control;
+		count = ARRAY_SIZE(fs210x_vol_control);
+	}
+
+	ret = snd_soc_add_component_controls(cmpnt, kctrl, count);
+	if (ret)
+		return ret;
+
+	/*
+	 * If the firmware has no scene or only init scene,
+	 * we skip adding this mixer control.
+	 */
+	if (fs210x->amp_lib.scene_count < 2)
+		return 0;
+
+	kctrl = fs210x_scene_control;
+	count = ARRAY_SIZE(fs210x_scene_control);
+
+	return snd_soc_add_component_controls(cmpnt, kctrl, count);
+}
+
+static int fs210x_probe(struct snd_soc_component *cmpnt)
+{
+	struct fs210x_priv *fs210x;
+	int ret;
+
+	fs210x = snd_soc_component_get_drvdata(cmpnt);
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	fs210x->amp_lib.dev   = fs210x->dev;
+	fs210x->amp_lib.devid = fs210x->devid;
+
+	ret = fs_amp_load_firmware(&fs210x->amp_lib, fs210x->pdata.fwm_name);
+	if (ret)
+		return ret;
+
+	ret = fs210x_add_mixer_controls(fs210x, cmpnt);
+	if (ret)
+		return ret;
+
+	mutex_lock(&fs210x->lock);
+	ret = fs210x_init_chip(fs210x);
+	mutex_unlock(&fs210x->lock);
+
+	return ret;
+}
+
+static void fs210x_remove(struct snd_soc_component *cmpnt)
+{
+	struct fs210x_priv *fs210x;
+
+	fs210x = snd_soc_component_get_drvdata(cmpnt);
+	if (!fs210x || !fs210x->dev)
+		return;
+
+	cancel_delayed_work_sync(&fs210x->start_work);
+	cancel_delayed_work_sync(&fs210x->fault_check_work);
+}
+
+#ifdef CONFIG_PM
+static int fs210x_suspend(struct snd_soc_component *cmpnt)
+{
+	struct fs210x_priv *fs210x;
+	int ret;
+
+	fs210x = snd_soc_component_get_drvdata(cmpnt);
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	regcache_cache_only(fs210x->regmap, true);
+
+	mutex_lock(&fs210x->lock);
+	fs210x->cur_scene = NULL;
+	fs210x->is_inited = false;
+	fs210x->is_playing = false;
+	fs210x->is_suspended = true;
+
+	gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+	fsleep(30000); /* >= 30ms */
+	mutex_unlock(&fs210x->lock);
+
+	cancel_delayed_work_sync(&fs210x->start_work);
+	cancel_delayed_work_sync(&fs210x->fault_check_work);
+
+	ret = regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to suspend: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fs210x_resume(struct snd_soc_component *cmpnt)
+{
+	struct fs210x_priv *fs210x;
+	int ret;
+
+	fs210x = snd_soc_component_get_drvdata(cmpnt);
+	if (!fs210x || !fs210x->dev)
+		return -EINVAL;
+
+	ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+	if (ret) {
+		dev_err(fs210x->dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
+	mutex_lock(&fs210x->lock);
+
+	fs210x->is_suspended = false;
+	ret = fs210x_init_chip(fs210x);
+
+	mutex_unlock(&fs210x->lock);
+
+	return ret;
+}
+#else
+#define fs210x_suspend NULL
+#define fs210x_resume NULL
+#endif // CONFIG_PM
+
+static bool fs210x_volatile_registers(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case FS210X_00H_STATUS ... FS210X_0FH_I2CADDR:
+	case FS210X_ABH_INTSTAT:
+	case FS210X_ACH_INTSTATR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct snd_soc_component_driver fs210x_soc_component_dev = {
+	.probe			= fs210x_probe,
+	.remove			= fs210x_remove,
+	.suspend		= fs210x_suspend,
+	.resume			= fs210x_resume,
+	.controls		= fs210x_controls,
+	.num_controls		= ARRAY_SIZE(fs210x_controls),
+	.dapm_widgets		= fs210x_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(fs210x_dapm_widgets),
+	.dapm_routes		= fs210x_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(fs210x_dapm_routes),
+};
+
+static const struct regmap_config fs210x_regmap = {
+	.reg_bits		= 8,
+	.val_bits		= 16,
+	.max_register		= FS210X_REG_MAX,
+	.val_format_endian	= REGMAP_ENDIAN_BIG,
+	.cache_type		= REGCACHE_MAPLE,
+	.volatile_reg		= fs210x_volatile_registers,
+};
+
+static int fs210x_detect_device(struct fs210x_priv *fs210x)
+{
+	u16 devid;
+	int ret;
+
+	ret = fs210x_reg_read(fs210x, FS210X_03H_DEVID, &devid);
+	if (ret)
+		return ret;
+
+	fs210x->devid = HI_U16(devid);
+
+	switch (fs210x->devid) {
+	case FS210X_DEVICE_ID:
+		dev_info(fs210x->dev, "FS2104 detected\n");
+		break;
+	case FS2105S_DEVICE_ID:
+		dev_info(fs210x->dev, "FS2105S detected\n");
+		break;
+	default:
+		dev_err(fs210x->dev, "DEVID: 0x%04X dismatch\n", devid);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int fs210x_parse_dts(struct fs210x_priv *fs210x,
+			    struct fs210x_platform_data *pdata)
+{
+	struct device_node *node = fs210x->dev->of_node;
+	int i, ret;
+
+	if (!node)
+		return 0;
+
+	ret = of_property_read_string(node, "firmware-name", &pdata->fwm_name);
+	if (ret)
+		pdata->fwm_name = FS210X_DEFAULT_FWM_NAME;
+
+	fs210x->gpio_sdz = devm_gpiod_get_optional(fs210x->dev,
+						   "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(fs210x->gpio_sdz))
+		return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->gpio_sdz),
+				     "Failed to get reset-gpios\n");
+
+	for (i = 0; i < FS210X_NUM_SUPPLIES; i++)
+		fs210x->supplies[i].supply = fs210x_supply_names[i];
+
+	ret = devm_regulator_bulk_get(fs210x->dev,
+				      ARRAY_SIZE(fs210x->supplies),
+				      fs210x->supplies);
+	if (ret)
+		return dev_err_probe(fs210x->dev, ret,
+				     "Failed to get supplies\n");
+
+	return 0;
+}
+
+static void fs210x_deinit(struct fs210x_priv *fs210x)
+{
+	gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+	fsleep(10000); /* >= 10ms */
+
+	regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+}
+
+static int fs210x_init(struct fs210x_priv *fs210x)
+{
+	int ret;
+
+	ret = fs210x_parse_dts(fs210x, &fs210x->pdata);
+	if (ret)
+		return ret;
+
+	fs210x->clk_bclk = devm_clk_get_optional(fs210x->dev, "bclk");
+	if (IS_ERR(fs210x->clk_bclk))
+		return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->clk_bclk),
+				     "Failed to get bclk\n");
+
+	ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+	if (ret)
+		return dev_err_probe(fs210x->dev, ret,
+				     "Failed to enable supplies\n");
+
+	/* Make sure the SDZ pin is pulled down enough time. */
+	fsleep(10000); /* >= 10ms */
+	gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); /* Deactivate */
+	fsleep(10000); /* >= 10ms */
+
+	ret = fs210x_detect_device(fs210x);
+	if (ret) {
+		fs210x_deinit(fs210x);
+		return ret;
+	}
+
+	fs210x->scene_id     = -1; /* Invalid scene */
+	fs210x->cur_scene    = NULL;
+	fs210x->is_playing   = false;
+	fs210x->is_inited    = false;
+	fs210x->is_suspended = false;
+	fs210x->check_interval_ms = FS210X_FAULT_CHECK_INTERVAL_MS;
+
+	INIT_DELAYED_WORK(&fs210x->fault_check_work, fs210x_fault_check_work);
+	INIT_DELAYED_WORK(&fs210x->start_work, fs210x_start_work);
+	mutex_init(&fs210x->lock);
+
+	return 0;
+}
+
+static int fs210x_register_snd_component(struct fs210x_priv *fs210x)
+{
+	struct snd_soc_dai_driver *dai_drv;
+	static int instance_id;
+	int ret;
+
+	dai_drv = devm_kmemdup(fs210x->dev, &fs210x_dai,
+			       sizeof(fs210x_dai), GFP_KERNEL);
+	if (!dai_drv)
+		return -ENOMEM;
+
+	dai_drv->name = devm_kasprintf(fs210x->dev,
+				       GFP_KERNEL, "%s-%d",
+				       dai_drv->name, instance_id);
+	if (!dai_drv->name)
+		return -ENOMEM;
+
+	instance_id++;
+
+	if (fs210x->devid == FS2105S_DEVICE_ID) {
+		dai_drv->playback.rates = FS2105S_RATES;
+		dai_drv->capture.rates  = FS2105S_RATES;
+	}
+
+	ret = snd_soc_register_component(fs210x->dev,
+					 &fs210x_soc_component_dev,
+					 dai_drv, 1);
+	return ret;
+}
+
+static ssize_t check_interval_ms_show(struct device *dev,
+				      struct device_attribute *devattr,
+				      char *buf)
+{
+	struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+
+	return sysfs_emit(buf, "%d\n", fs210x->check_interval_ms);
+}
+
+static ssize_t check_interval_ms_store(struct device *dev,
+				       struct device_attribute *devattr,
+				       const char *buf,
+				       size_t count)
+{
+	struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+	int ret;
+
+	ret = kstrtouint(buf, 10, &fs210x->check_interval_ms);
+	if (ret)
+		return -EINVAL;
+
+	return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_interval_ms);
+
+static struct attribute *fs210x_attrs[] = {
+	&dev_attr_check_interval_ms.attr,
+	NULL,
+};
+
+static struct attribute_group fs210x_attr_group = {
+	.attrs = fs210x_attrs,
+};
+
+static int fs210x_i2c_probe(struct i2c_client *client)
+{
+	struct fs210x_priv *fs210x;
+	int ret;
+
+	fs210x = devm_kzalloc(&client->dev, sizeof(*fs210x), GFP_KERNEL);
+	if (!fs210x)
+		return -ENOMEM;
+
+	fs210x->i2c = client;
+	fs210x->dev = &client->dev;
+	i2c_set_clientdata(client, fs210x);
+
+	fs210x->regmap = devm_regmap_init_i2c(client, &fs210x_regmap);
+	if (IS_ERR(fs210x->regmap))
+		return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->regmap),
+				     "Failed to get regmap\n");
+
+	ret = fs210x_init(fs210x);
+	if (ret)
+		return ret;
+
+	ret = devm_device_add_group(fs210x->dev, &fs210x_attr_group);
+	if (ret) {
+		fs210x_deinit(fs210x);
+		return dev_err_probe(fs210x->dev, ret,
+				     "Failed to create sysfs group\n");
+	}
+
+	ret = fs210x_register_snd_component(fs210x);
+	if (ret) {
+		fs210x_deinit(fs210x);
+		return dev_err_probe(fs210x->dev, ret,
+				     "Failed to register component\n");
+	}
+
+	return 0;
+}
+
+static void fs210x_i2c_remove(struct i2c_client *client)
+{
+	struct fs210x_priv *fs210x = i2c_get_clientdata(client);
+
+	snd_soc_unregister_component(fs210x->dev);
+	fs210x_deinit(fs210x);
+}
+
+static const struct i2c_device_id fs210x_i2c_id[] = {
+	{ "fs2104" },
+	{ "fs2105s" },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id);
+
+static const struct of_device_id fs210x_of_match[] = {
+	{ .compatible = "foursemi,fs2105s", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, fs210x_of_match);
+
+static struct i2c_driver fs210x_i2c_driver = {
+	.driver = {
+		.name = "fs210x",
+		.of_match_table = fs210x_of_match,
+	},
+	.id_table = fs210x_i2c_id,
+	.probe    = fs210x_i2c_probe,
+	.remove   = fs210x_i2c_remove,
+};
+
+module_i2c_driver(fs210x_i2c_driver);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FS2104/5S Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs210x.h b/sound/soc/codecs/fs210x.h
new file mode 100644
index 0000000..78e1760
--- /dev/null
+++ b/sound/soc/codecs/fs210x.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * fs210x.h -- Driver for the FS2104/5S Audio Amplifier
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS210X_H__
+#define __FS210X_H__
+
+#define FS210X_00H_STATUS		0x00
+#define FS210X_03H_DEVID		0x03
+#define FS210X_05H_ANASTAT		0x05
+#define FS210X_06H_DIGSTAT		0x06
+#define FS210X_0BH_ACCKEY		0x0B
+#define FS210X_0FH_I2CADDR		0x0F
+#define FS210X_10H_PWRCTRL		0x10
+#define FS210X_11H_SYSCTRL		0x11
+#define FS210X_17H_I2SCTRL		0x17
+#define FS210X_30H_DACCTRL		0x30
+#define FS210X_39H_LVOLCTRL		0x39
+#define FS210X_3AH_RVOLCTRL		0x3A
+#define FS210X_42H_DACEQWL		0x42
+#define FS210X_46H_DACEQA		0x46
+#define FS210X_A1H_PLLCTRL1		0xA1
+#define FS210X_A2H_PLLCTRL2		0xA2
+#define FS210X_A3H_PLLCTRL3		0xA3
+#define FS210X_ABH_INTSTAT		0xAB
+#define FS210X_ACH_INTSTATR		0xAC
+
+#define FS210X_05H_PVDD_SHIFT		14
+#define FS210X_05H_PVDD_MASK		BIT(14)
+#define FS210X_05H_OCDL_SHIFT		13
+#define FS210X_05H_OCDL_MASK		BIT(13)
+#define FS210X_05H_UVDL_SHIFT		12
+#define FS210X_05H_UVDL_MASK		BIT(12)
+#define FS210X_05H_OVDL_SHIFT		11
+#define FS210X_05H_OVDL_MASK		BIT(11)
+#define FS210X_05H_OTPDL_SHIFT		10
+#define FS210X_05H_OTPDL_MASK		BIT(10)
+#define FS210X_05H_OCRDL_SHIFT		9
+#define FS210X_05H_OCRDL_MASK		BIT(9)
+#define FS210X_05H_OCLDL_SHIFT		8
+#define FS210X_05H_OCLDL_MASK		BIT(8)
+#define FS210X_05H_DCRDL_SHIFT		7
+#define FS210X_05H_DCRDL_MASK		BIT(7)
+#define FS210X_05H_DCLDL_SHIFT		6
+#define FS210X_05H_DCLDL_MASK		BIT(6)
+#define FS210X_05H_SRDL_SHIFT		5
+#define FS210X_05H_SRDL_MASK		BIT(5)
+#define FS210X_05H_OTWDL_SHIFT		4
+#define FS210X_05H_OTWDL_MASK		BIT(4)
+#define FS210X_05H_AMPS_SHIFT		3
+#define FS210X_05H_AMPS_MASK		BIT(3)
+#define FS210X_05H_PLLS_SHIFT		1
+#define FS210X_05H_PLLS_MASK		BIT(1)
+#define FS210X_05H_ANAS_SHIFT		0
+#define FS210X_05H_ANAS_MASK		BIT(0)
+#define FS210X_17H_I2SSR_SHIFT		12
+#define FS210X_17H_I2SSR_MASK		GENMASK(15, 12)
+#define FS210X_30H_RMUTE_SHIFT		8
+#define FS210X_30H_LMUTE_SHIFT		4
+
+#define FS210X_0BH_ACCKEY_ON		0x0091
+#define FS210X_0BH_ACCKEY_OFF		0x0000
+#define FS210X_10H_I2C_RESET		0x0002
+#define FS210X_11H_DPS_HIZ		0x0100
+#define FS210X_11H_DPS_PWDN		0x0000
+#define FS210X_11H_DPS_PLAY		0x0300
+#define FS210X_46H_CAM_BURST_L		0x8000
+#define FS210X_46H_CAM_BURST_R		0x8200
+#define FS2105S_46H_CAM_BURST_W		0x8400
+#define FS210X_46H_CAM_CLEAR		0x0000
+
+#endif /* __FS210X_H__ */
diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h
index fb4b96c..10ad682 100644
--- a/sound/soc/codecs/lpass-macro-common.h
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -29,6 +29,7 @@ enum lpass_codec_version {
 	LPASS_CODEC_VERSION_2_6,
 	LPASS_CODEC_VERSION_2_7,
 	LPASS_CODEC_VERSION_2_8,
+	LPASS_CODEC_VERSION_2_9,
 };
 
 struct lpass_macro {
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index a49551f..2e1b779 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -1485,6 +1485,8 @@ static void va_macro_set_lpass_codec_version(struct va_macro *va)
 		version = LPASS_CODEC_VERSION_2_7;
 	if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x80 || core_id_2 == 0x81))
 		version = LPASS_CODEC_VERSION_2_8;
+	if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x90 || core_id_2 == 0x91))
+		version = LPASS_CODEC_VERSION_2_9;
 
 	if (version == LPASS_CODEC_VERSION_UNKNOWN)
 		dev_warn(va->dev, "Unknown Codec version, ID: %02x / %02x / %02x\n",
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index da6adb3..187805b 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -2690,6 +2690,7 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp)
 	case LPASS_CODEC_VERSION_2_6:
 	case LPASS_CODEC_VERSION_2_7:
 	case LPASS_CODEC_VERSION_2_8:
+	case LPASS_CODEC_VERSION_2_9:
 		widgets = wsa_macro_dapm_widgets_v2_5;
 		num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5);
 		break;
@@ -2838,6 +2839,7 @@ static int wsa_macro_probe(struct platform_device *pdev)
 	case LPASS_CODEC_VERSION_2_6:
 	case LPASS_CODEC_VERSION_2_7:
 	case LPASS_CODEC_VERSION_2_8:
+	case LPASS_CODEC_VERSION_2_9:
 		wsa->reg_layout = &wsa_codec_v2_5;
 		def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5);
 		reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c
new file mode 100644
index 0000000..b68a528
--- /dev/null
+++ b/sound/soc/codecs/pcm1754.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM1754 DAC ASoC codec driver
+ *
+ * Copyright (c) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
+ * Copyright (c) 2025 Stefan Kerkmann <s.kerkmann@pengutronix.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct pcm1754_priv {
+	unsigned int format;
+	struct gpio_desc *gpiod_mute;
+	struct gpio_desc *gpiod_format;
+};
+
+static int pcm1754_set_dai_fmt(struct snd_soc_dai *codec_dai,
+				   unsigned int format)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+
+	priv->format = format;
+
+	return 0;
+}
+
+static int pcm1754_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *codec_dai)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+	int format;
+
+	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_RIGHT_J:
+		switch (params_width(params)) {
+		case 16:
+			format = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		switch (params_width(params)) {
+		case 16:
+			fallthrough;
+		case 24:
+			format = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		dev_err(component->dev, "Invalid DAI format\n");
+		return -EINVAL;
+	}
+
+	gpiod_set_value_cansleep(priv->gpiod_format, format);
+
+	return 0;
+}
+
+static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct pcm1754_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+	gpiod_set_value_cansleep(priv->gpiod_mute, mute);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1754_dai_ops = {
+	.set_fmt = pcm1754_set_dai_fmt,
+	.hw_params = pcm1754_hw_params,
+	.mute_stream = pcm1754_mute_stream,
+};
+
+static const struct snd_soc_dai_driver pcm1754_dai = {
+	.name = "pcm1754",
+	.playback = {
+		.stream_name	= "Playback",
+		.channels_min	= 2,
+		.channels_max	= 2,
+		.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+		.rate_min	= 5000,
+		.rate_max	= 200000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+	},
+	.ops = &pcm1754_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget pcm1754_dapm_widgets[] = {
+	SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 0, 0),
+
+	SND_SOC_DAPM_DAC("DAC1", "Channel 1 Playback", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("DAC2", "Channel 2 Playback", SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_OUTPUT("VOUTL"),
+	SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm1754_dapm_routes[] = {
+	{ "DAC1", NULL, "Playback" },
+	{ "DAC2", NULL, "Playback" },
+
+	{ "DAC1", NULL, "VCC" },
+	{ "DAC2", NULL, "VCC" },
+
+	{ "VOUTL", NULL, "DAC1" },
+	{ "VOUTR", NULL, "DAC2" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1754 = {
+	.dapm_widgets = pcm1754_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(pcm1754_dapm_widgets),
+	.dapm_routes = pcm1754_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(pcm1754_dapm_routes),
+};
+
+static int pcm1754_probe(struct platform_device *pdev)
+{
+	struct pcm1754_priv *priv;
+	struct device *dev = &pdev->dev;
+	struct snd_soc_dai_driver *dai_drv;
+	int ret;
+
+	dai_drv = devm_kmemdup(dev, &pcm1754_dai, sizeof(*dai_drv), GFP_KERNEL);
+	if (!dai_drv)
+		return -ENOMEM;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->gpiod_mute = devm_gpiod_get_optional(dev, "mute", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->gpiod_mute))
+		return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute),
+					 "failed to get mute gpio");
+
+	priv->gpiod_format = devm_gpiod_get_optional(dev, "format", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->gpiod_format))
+		return dev_err_probe(dev, PTR_ERR(priv->gpiod_format),
+					 "failed to get format gpio");
+
+	dev_set_drvdata(dev, priv);
+
+	ret = devm_snd_soc_register_component(
+		&pdev->dev, &soc_component_dev_pcm1754, dai_drv, 1);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register");
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1754_of_match[] = {
+	{ .compatible = "ti,pcm1754" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pcm1754_of_match);
+#endif
+
+static struct platform_driver pcm1754_codec_driver = {
+	.driver = {
+		.name = "pcm1754-codec",
+		.of_match_table = of_match_ptr(pcm1754_of_match),
+	},
+	.probe = pcm1754_probe,
+};
+
+module_platform_driver(pcm1754_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1754 driver");
+MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+MODULE_AUTHOR("Stefan Kerkmann <s.kerkmann@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index 75af122..08cc52b 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -1353,8 +1353,8 @@ static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev,
 		return 0;
 	}
 
-	pcmdev_controls = devm_kzalloc(pcm_dev->dev,
-		nr_chn * sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+	pcmdev_controls = devm_kcalloc(pcm_dev->dev, nr_chn,
+				       sizeof(struct snd_kcontrol_new), GFP_KERNEL);
 	if (!pcmdev_controls)
 		return -ENOMEM;
 
diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
new file mode 100644
index 0000000..4ed09fb
--- /dev/null
+++ b/sound/soc/codecs/pm4125-sdw.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright, 2025 Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include "pm4125.h"
+
+static struct pm4125_sdw_ch_info pm4125_sdw_rx_ch_info[] = {
+	WCD_SDW_CH(PM4125_HPH_L, PM4125_HPH_PORT, BIT(0)),
+	WCD_SDW_CH(PM4125_HPH_R, PM4125_HPH_PORT, BIT(1)),
+};
+
+static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
+	WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(0)),
+	WCD_SDW_CH(PM4125_ADC2, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(1)),
+};
+
+static struct sdw_dpn_prop pm4125_dpn_prop[PM4125_MAX_SWR_PORTS] = {
+	{
+		.num = 1,
+		.type = SDW_DPN_SIMPLE,
+		.min_ch = 1,
+		.max_ch = 8,
+		.simple_ch_prep_sm = true,
+	}, {
+		.num = 2,
+		.type = SDW_DPN_SIMPLE,
+		.min_ch = 1,
+		.max_ch = 4,
+		.simple_ch_prep_sm = true,
+	}
+};
+
+struct device *pm4125_sdw_device_get(struct device_node *np)
+{
+	return bus_find_device_by_of_node(&sdw_bus_type, np);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_device_get);
+
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *priv, struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+	unsigned long ch_mask;
+	int i, j;
+
+	priv->sconfig.ch_count = 1;
+	priv->active_ports = 0;
+	for (i = 0; i < PM4125_MAX_SWR_PORTS; i++) {
+		ch_mask = priv->port_config[i].ch_mask;
+		if (!ch_mask)
+			continue;
+
+		for_each_set_bit(j, &ch_mask, 4)
+			priv->sconfig.ch_count++;
+
+		port_config[priv->active_ports] = priv->port_config[i];
+		priv->active_ports++;
+	}
+
+	priv->sconfig.bps = 1;
+	priv->sconfig.frame_rate = params_rate(params);
+	priv->sconfig.direction = priv->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
+	priv->sconfig.type = SDW_STREAM_PCM;
+
+	return sdw_stream_add_slave(priv->sdev, &priv->sconfig, &port_config[0], priv->active_ports,
+				    priv->sruntime);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_hw_params);
+
+static int pm4125_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
+{
+	struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+
+	if (priv->regmap && status == SDW_SLAVE_ATTACHED) {
+		/* Write out any cached changes that happened between probe and attach */
+		regcache_cache_only(priv->regmap, false);
+		return regcache_sync(priv->regmap);
+	}
+
+	return 0;
+}
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering the first irq of the slave_irq
+ * irq domain, which then will be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int pm4125_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status)
+{
+	struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+	struct irq_domain *slave_irq = priv->slave_irq;
+	u32 sts1, sts2, sts3;
+
+	do {
+		handle_nested_irq(irq_find_mapping(slave_irq, 0));
+		regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_0, &sts1);
+		regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_1, &sts2);
+		regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_2, &sts3);
+
+	} while (sts1 || sts2 || sts3);
+
+	return IRQ_HANDLED;
+}
+
+static const struct reg_default pm4125_defaults[] = {
+	{ PM4125_ANA_MICBIAS_MICB_1_2_EN,        0x01 },
+	{ PM4125_ANA_MICBIAS_MICB_3_EN,          0x00 },
+	{ PM4125_ANA_MICBIAS_LDO_1_SETTING,      0x21 },
+	{ PM4125_ANA_MICBIAS_LDO_1_CTRL,         0x01 },
+	{ PM4125_ANA_TX_AMIC1,                   0x00 },
+	{ PM4125_ANA_TX_AMIC2,                   0x00 },
+	{ PM4125_ANA_MBHC_MECH,                  0x39 },
+	{ PM4125_ANA_MBHC_ELECT,                 0x08 },
+	{ PM4125_ANA_MBHC_ZDET,                  0x10 },
+	{ PM4125_ANA_MBHC_RESULT_1,              0x00 },
+	{ PM4125_ANA_MBHC_RESULT_2,              0x00 },
+	{ PM4125_ANA_MBHC_RESULT_3,              0x00 },
+	{ PM4125_ANA_MBHC_BTN0_ZDET_VREF1,       0x00 },
+	{ PM4125_ANA_MBHC_BTN1_ZDET_VREF2,       0x10 },
+	{ PM4125_ANA_MBHC_BTN2_ZDET_VREF3,       0x20 },
+	{ PM4125_ANA_MBHC_BTN3_ZDET_DBG_400,     0x30 },
+	{ PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400,    0x40 },
+	{ PM4125_ANA_MBHC_MICB2_RAMP,            0x00 },
+	{ PM4125_ANA_MBHC_CTL_1,                 0x02 },
+	{ PM4125_ANA_MBHC_CTL_2,                 0x05 },
+	{ PM4125_ANA_MBHC_PLUG_DETECT_CTL,       0xE9 },
+	{ PM4125_ANA_MBHC_ZDET_ANA_CTL,          0x0F },
+	{ PM4125_ANA_MBHC_ZDET_RAMP_CTL,         0x00 },
+	{ PM4125_ANA_MBHC_FSM_STATUS,            0x00 },
+	{ PM4125_ANA_MBHC_ADC_RESULT,            0x00 },
+	{ PM4125_ANA_MBHC_CTL_CLK,               0x30 },
+	{ PM4125_ANA_MBHC_ZDET_CALIB_RESULT,     0x00 },
+	{ PM4125_ANA_NCP_EN,                     0x00 },
+	{ PM4125_ANA_NCP_VCTRL,                  0xA7 },
+	{ PM4125_ANA_HPHPA_CNP_CTL_1,            0x54 },
+	{ PM4125_ANA_HPHPA_CNP_CTL_2,            0x2B },
+	{ PM4125_ANA_HPHPA_PA_STATUS,            0x00 },
+	{ PM4125_ANA_HPHPA_FSM_CLK,              0x12 },
+	{ PM4125_ANA_HPHPA_L_GAIN,               0x00 },
+	{ PM4125_ANA_HPHPA_R_GAIN,               0x00 },
+	{ PM4125_SWR_HPHPA_HD2,                  0x1B },
+	{ PM4125_ANA_HPHPA_SPARE_CTL,            0x02 },
+	{ PM4125_ANA_SURGE_EN,                   0x38 },
+	{ PM4125_ANA_COMBOPA_CTL,                0x35 },
+	{ PM4125_ANA_COMBOPA_CTL_4,              0x84 },
+	{ PM4125_ANA_COMBOPA_CTL_5,              0x05 },
+	{ PM4125_ANA_RXLDO_CTL,                  0x86 },
+	{ PM4125_ANA_MBIAS_EN,                   0x00 },
+	{ PM4125_DIG_SWR_CHIP_ID0,               0x00 },
+	{ PM4125_DIG_SWR_CHIP_ID1,               0x00 },
+	{ PM4125_DIG_SWR_CHIP_ID2,               0x0C },
+	{ PM4125_DIG_SWR_CHIP_ID3,               0x01 },
+	{ PM4125_DIG_SWR_SWR_TX_CLK_RATE,        0x00 },
+	{ PM4125_DIG_SWR_CDC_RST_CTL,            0x03 },
+	{ PM4125_DIG_SWR_TOP_CLK_CFG,            0x00 },
+	{ PM4125_DIG_SWR_CDC_RX_CLK_CTL,         0x00 },
+	{ PM4125_DIG_SWR_CDC_TX_CLK_CTL,         0x33 },
+	{ PM4125_DIG_SWR_SWR_RST_EN,             0x00 },
+	{ PM4125_DIG_SWR_CDC_RX_RST,             0x00 },
+	{ PM4125_DIG_SWR_CDC_RX0_CTL,            0xFC },
+	{ PM4125_DIG_SWR_CDC_RX1_CTL,            0xFC },
+	{ PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,    0x00 },
+	{ PM4125_DIG_SWR_CDC_COMP_CTL_0,         0x00 },
+	{ PM4125_DIG_SWR_CDC_RX_DELAY_CTL,       0x66 },
+	{ PM4125_DIG_SWR_CDC_RX_GAIN_0,          0x55 },
+	{ PM4125_DIG_SWR_CDC_RX_GAIN_1,          0xA9 },
+	{ PM4125_DIG_SWR_CDC_RX_GAIN_CTL,        0x00 },
+	{ PM4125_DIG_SWR_CDC_TX0_CTL,            0x68 },
+	{ PM4125_DIG_SWR_CDC_TX1_CTL,            0x68 },
+	{ PM4125_DIG_SWR_CDC_TX_RST,             0x00 },
+	{ PM4125_DIG_SWR_CDC_REQ0_CTL,           0x01 },
+	{ PM4125_DIG_SWR_CDC_REQ1_CTL,           0x01 },
+	{ PM4125_DIG_SWR_CDC_RST,                0x00 },
+	{ PM4125_DIG_SWR_CDC_AMIC_CTL,           0x02 },
+	{ PM4125_DIG_SWR_CDC_DMIC_CTL,           0x00 },
+	{ PM4125_DIG_SWR_CDC_DMIC1_CTL,          0x00 },
+	{ PM4125_DIG_SWR_CDC_DMIC1_RATE,         0x01 },
+	{ PM4125_DIG_SWR_PDM_WD_CTL0,            0x00 },
+	{ PM4125_DIG_SWR_PDM_WD_CTL1,            0x00 },
+	{ PM4125_DIG_SWR_INTR_MODE,              0x00 },
+	{ PM4125_DIG_SWR_INTR_MASK_0,            0xFF },
+	{ PM4125_DIG_SWR_INTR_MASK_1,            0x7F },
+	{ PM4125_DIG_SWR_INTR_MASK_2,            0x0C },
+	{ PM4125_DIG_SWR_INTR_STATUS_0,          0x00 },
+	{ PM4125_DIG_SWR_INTR_STATUS_1,          0x00 },
+	{ PM4125_DIG_SWR_INTR_STATUS_2,          0x00 },
+	{ PM4125_DIG_SWR_INTR_CLEAR_0,           0x00 },
+	{ PM4125_DIG_SWR_INTR_CLEAR_1,           0x00 },
+	{ PM4125_DIG_SWR_INTR_CLEAR_2,           0x00 },
+	{ PM4125_DIG_SWR_INTR_LEVEL_0,           0x00 },
+	{ PM4125_DIG_SWR_INTR_LEVEL_1,           0x2A },
+	{ PM4125_DIG_SWR_INTR_LEVEL_2,           0x00 },
+	{ PM4125_DIG_SWR_CDC_CONN_RX0_CTL,       0x00 },
+	{ PM4125_DIG_SWR_CDC_CONN_RX1_CTL,       0x00 },
+	{ PM4125_DIG_SWR_LOOP_BACK_MODE,         0x00 },
+	{ PM4125_DIG_SWR_DRIVE_STRENGTH_0,       0x00 },
+	{ PM4125_DIG_SWR_DIG_DEBUG_CTL,          0x00 },
+	{ PM4125_DIG_SWR_DIG_DEBUG_EN,           0x00 },
+	{ PM4125_DIG_SWR_DEM_BYPASS_DATA0,       0x55 },
+	{ PM4125_DIG_SWR_DEM_BYPASS_DATA1,       0x55 },
+	{ PM4125_DIG_SWR_DEM_BYPASS_DATA2,       0x55 },
+	{ PM4125_DIG_SWR_DEM_BYPASS_DATA3,       0x01 },
+};
+
+static bool pm4125_rdwr_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case PM4125_ANA_MICBIAS_MICB_1_2_EN:
+	case PM4125_ANA_MICBIAS_MICB_3_EN:
+	case PM4125_ANA_MICBIAS_LDO_1_SETTING:
+	case PM4125_ANA_MICBIAS_LDO_1_CTRL:
+	case PM4125_ANA_TX_AMIC1:
+	case PM4125_ANA_TX_AMIC2:
+	case PM4125_ANA_MBHC_MECH:
+	case PM4125_ANA_MBHC_ELECT:
+	case PM4125_ANA_MBHC_ZDET:
+	case PM4125_ANA_MBHC_BTN0_ZDET_VREF1:
+	case PM4125_ANA_MBHC_BTN1_ZDET_VREF2:
+	case PM4125_ANA_MBHC_BTN2_ZDET_VREF3:
+	case PM4125_ANA_MBHC_BTN3_ZDET_DBG_400:
+	case PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400:
+	case PM4125_ANA_MBHC_MICB2_RAMP:
+	case PM4125_ANA_MBHC_CTL_1:
+	case PM4125_ANA_MBHC_CTL_2:
+	case PM4125_ANA_MBHC_PLUG_DETECT_CTL:
+	case PM4125_ANA_MBHC_ZDET_ANA_CTL:
+	case PM4125_ANA_MBHC_ZDET_RAMP_CTL:
+	case PM4125_ANA_MBHC_CTL_CLK:
+	case PM4125_ANA_NCP_EN:
+	case PM4125_ANA_NCP_VCTRL:
+	case PM4125_ANA_HPHPA_CNP_CTL_1:
+	case PM4125_ANA_HPHPA_CNP_CTL_2:
+	case PM4125_ANA_HPHPA_FSM_CLK:
+	case PM4125_ANA_HPHPA_L_GAIN:
+	case PM4125_ANA_HPHPA_R_GAIN:
+	case PM4125_ANA_HPHPA_SPARE_CTL:
+	case PM4125_SWR_HPHPA_HD2:
+	case PM4125_ANA_SURGE_EN:
+	case PM4125_ANA_COMBOPA_CTL:
+	case PM4125_ANA_COMBOPA_CTL_4:
+	case PM4125_ANA_COMBOPA_CTL_5:
+	case PM4125_ANA_RXLDO_CTL:
+	case PM4125_ANA_MBIAS_EN:
+	case PM4125_DIG_SWR_SWR_TX_CLK_RATE:
+	case PM4125_DIG_SWR_CDC_RST_CTL:
+	case PM4125_DIG_SWR_TOP_CLK_CFG:
+	case PM4125_DIG_SWR_CDC_RX_CLK_CTL:
+	case PM4125_DIG_SWR_CDC_TX_CLK_CTL:
+	case PM4125_DIG_SWR_SWR_RST_EN:
+	case PM4125_DIG_SWR_CDC_RX_RST:
+	case PM4125_DIG_SWR_CDC_RX0_CTL:
+	case PM4125_DIG_SWR_CDC_RX1_CTL:
+	case PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1:
+	case PM4125_DIG_SWR_CDC_COMP_CTL_0:
+	case PM4125_DIG_SWR_CDC_RX_DELAY_CTL:
+	case PM4125_DIG_SWR_CDC_RX_GAIN_0:
+	case PM4125_DIG_SWR_CDC_RX_GAIN_1:
+	case PM4125_DIG_SWR_CDC_RX_GAIN_CTL:
+	case PM4125_DIG_SWR_CDC_TX0_CTL:
+	case PM4125_DIG_SWR_CDC_TX1_CTL:
+	case PM4125_DIG_SWR_CDC_TX_RST:
+	case PM4125_DIG_SWR_CDC_REQ0_CTL:
+	case PM4125_DIG_SWR_CDC_REQ1_CTL:
+	case PM4125_DIG_SWR_CDC_RST:
+	case PM4125_DIG_SWR_CDC_AMIC_CTL:
+	case PM4125_DIG_SWR_CDC_DMIC_CTL:
+	case PM4125_DIG_SWR_CDC_DMIC1_CTL:
+	case PM4125_DIG_SWR_CDC_DMIC1_RATE:
+	case PM4125_DIG_SWR_PDM_WD_CTL0:
+	case PM4125_DIG_SWR_PDM_WD_CTL1:
+	case PM4125_DIG_SWR_INTR_MODE:
+	case PM4125_DIG_SWR_INTR_MASK_0:
+	case PM4125_DIG_SWR_INTR_MASK_1:
+	case PM4125_DIG_SWR_INTR_MASK_2:
+	case PM4125_DIG_SWR_INTR_CLEAR_0:
+	case PM4125_DIG_SWR_INTR_CLEAR_1:
+	case PM4125_DIG_SWR_INTR_CLEAR_2:
+	case PM4125_DIG_SWR_INTR_LEVEL_0:
+	case PM4125_DIG_SWR_INTR_LEVEL_1:
+	case PM4125_DIG_SWR_INTR_LEVEL_2:
+	case PM4125_DIG_SWR_CDC_CONN_RX0_CTL:
+	case PM4125_DIG_SWR_CDC_CONN_RX1_CTL:
+	case PM4125_DIG_SWR_LOOP_BACK_MODE:
+	case PM4125_DIG_SWR_DRIVE_STRENGTH_0:
+	case PM4125_DIG_SWR_DIG_DEBUG_CTL:
+	case PM4125_DIG_SWR_DIG_DEBUG_EN:
+	case PM4125_DIG_SWR_DEM_BYPASS_DATA0:
+	case PM4125_DIG_SWR_DEM_BYPASS_DATA1:
+	case PM4125_DIG_SWR_DEM_BYPASS_DATA2:
+	case PM4125_DIG_SWR_DEM_BYPASS_DATA3:
+		return true;
+	}
+
+	return false;
+}
+
+static bool pm4125_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case PM4125_ANA_MBHC_RESULT_1:
+	case PM4125_ANA_MBHC_RESULT_2:
+	case PM4125_ANA_MBHC_RESULT_3:
+	case PM4125_ANA_MBHC_FSM_STATUS:
+	case PM4125_ANA_MBHC_ADC_RESULT:
+	case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+	case PM4125_ANA_HPHPA_PA_STATUS:
+	case PM4125_DIG_SWR_CHIP_ID0:
+	case PM4125_DIG_SWR_CHIP_ID1:
+	case PM4125_DIG_SWR_CHIP_ID2:
+	case PM4125_DIG_SWR_CHIP_ID3:
+	case PM4125_DIG_SWR_INTR_STATUS_0:
+	case PM4125_DIG_SWR_INTR_STATUS_1:
+	case PM4125_DIG_SWR_INTR_STATUS_2:
+		return true;
+	}
+	return pm4125_rdwr_register(dev, reg);
+}
+
+static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case PM4125_ANA_MBHC_RESULT_1:
+	case PM4125_ANA_MBHC_RESULT_2:
+	case PM4125_ANA_MBHC_RESULT_3:
+	case PM4125_ANA_MBHC_FSM_STATUS:
+	case PM4125_ANA_MBHC_ADC_RESULT:
+	case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+	case PM4125_ANA_HPHPA_PA_STATUS:
+	case PM4125_DIG_SWR_CHIP_ID0:
+	case PM4125_DIG_SWR_CHIP_ID1:
+	case PM4125_DIG_SWR_CHIP_ID2:
+	case PM4125_DIG_SWR_CHIP_ID3:
+	case PM4125_DIG_SWR_INTR_STATUS_0:
+	case PM4125_DIG_SWR_INTR_STATUS_1:
+	case PM4125_DIG_SWR_INTR_STATUS_2:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config pm4125_regmap_config = {
+	.name = "pm4125_csr",
+	.reg_bits = 32,
+	.val_bits = 8,
+	.cache_type = REGCACHE_MAPLE,
+	.reg_defaults = pm4125_defaults,
+	.num_reg_defaults = ARRAY_SIZE(pm4125_defaults),
+	.max_register = PM4125_MAX_REGISTER,
+	.readable_reg = pm4125_readable_register,
+	.writeable_reg = pm4125_rdwr_register,
+	.volatile_reg = pm4125_volatile_register,
+};
+
+static const struct sdw_slave_ops pm4125_slave_ops = {
+	.update_status = pm4125_update_status,
+	.interrupt_callback = pm4125_interrupt_callback,
+};
+
+static int pm4125_sdw_component_bind(struct device *dev, struct device *master, void *data)
+{
+	pm_runtime_set_autosuspend_delay(dev, 3000);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+
+static void pm4125_sdw_component_unbind(struct device *dev, struct device *master, void *data)
+{
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct component_ops pm4125_sdw_component_ops = {
+	.bind = pm4125_sdw_component_bind,
+	.unbind = pm4125_sdw_component_unbind,
+};
+
+static int pm4125_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+	struct device *dev = &pdev->dev;
+	struct pm4125_sdw_priv *priv;
+	u8 master_ch_mask[PM4125_MAX_SWR_CH_IDS];
+	int master_ch_mask_size = 0;
+	int ret, i;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* Port map index starts at 0, however the data port for this codec starts at index 1 */
+	if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+		priv->is_tx = true;
+		ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+						 &pdev->m_port_map[1], PM4125_MAX_TX_SWR_PORTS);
+	} else {
+		ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+						 &pdev->m_port_map[1], PM4125_MAX_SWR_PORTS);
+	}
+
+	if (ret < 0)
+		dev_info(dev, "Error getting static port mapping for %s (%d)\n",
+			 priv->is_tx ? "TX" : "RX", ret);
+
+	priv->sdev = pdev;
+	dev_set_drvdata(dev, priv);
+
+	pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF |
+				   SDW_SCP_INT1_BUS_CLASH |
+				   SDW_SCP_INT1_PARITY;
+	pdev->prop.lane_control_support = true;
+	pdev->prop.simple_clk_stop_capable = true;
+
+	memset(master_ch_mask, 0, PM4125_MAX_SWR_CH_IDS);
+
+	if (priv->is_tx) {
+		master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+								 "qcom,tx-channel-mapping");
+
+		if (master_ch_mask_size)
+			ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+							master_ch_mask, master_ch_mask_size);
+	} else {
+		master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+								 "qcom,rx-channel-mapping");
+
+		if (master_ch_mask_size)
+			ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+							master_ch_mask, master_ch_mask_size);
+	}
+
+	if (ret < 0)
+		dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+	if (priv->is_tx) {
+		pdev->prop.source_ports = GENMASK(PM4125_MAX_TX_SWR_PORTS, 0);
+		pdev->prop.src_dpn_prop = pm4125_dpn_prop;
+		priv->ch_info = &pm4125_sdw_tx_ch_info[0];
+
+		for (i = 0; i < master_ch_mask_size; i++)
+			priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+
+		pdev->prop.wake_capable = true;
+
+		priv->regmap = devm_regmap_init_sdw(pdev, &pm4125_regmap_config);
+		if (IS_ERR(priv->regmap))
+			return dev_err_probe(dev, PTR_ERR(priv->regmap), "regmap init failed\n");
+
+		/* Start in cache-only until device is enumerated */
+		regcache_cache_only(priv->regmap, true);
+	} else {
+		pdev->prop.sink_ports = GENMASK(PM4125_MAX_SWR_PORTS - 1, 0);
+		pdev->prop.sink_dpn_prop = pm4125_dpn_prop;
+		priv->ch_info = &pm4125_sdw_rx_ch_info[0];
+
+		for (i = 0; i < master_ch_mask_size; i++)
+			priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+	}
+
+	ret = component_add(dev, &pm4125_sdw_component_ops);
+	if (ret)
+		return ret;
+
+	/* Set suspended until aggregate device is bind */
+	pm_runtime_set_suspended(dev);
+
+	return 0;
+}
+
+static int pm4125_remove(struct sdw_slave *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	component_del(dev, &pm4125_sdw_component_ops);
+
+	return 0;
+}
+
+static const struct sdw_device_id pm4125_slave_id[] = {
+	SDW_SLAVE_ENTRY(0x0217, 0x10c, 0), /* Soundwire pm4125 RX/TX Device ID */
+	{ }
+};
+MODULE_DEVICE_TABLE(sdw, pm4125_slave_id);
+
+static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev)
+{
+	struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->regmap) {
+		regcache_cache_only(priv->regmap, true);
+		regcache_mark_dirty(priv->regmap);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev)
+{
+	struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->regmap) {
+		regcache_cache_only(priv->regmap, false);
+		regcache_sync(priv->regmap);
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm4125_sdw_pm_ops = {
+	SET_RUNTIME_PM_OPS(pm4125_sdw_runtime_suspend, pm4125_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver pm4125_codec_driver = {
+	.probe = pm4125_probe,
+	.remove = pm4125_remove,
+	.ops = &pm4125_slave_ops,
+	.id_table = pm4125_slave_id,
+	.driver = {
+		.name = "pm4125-codec",
+		.pm = &pm4125_sdw_pm_ops,
+	}
+};
+module_sdw_driver(pm4125_codec_driver);
+
+MODULE_DESCRIPTION("PM4125 SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
new file mode 100644
index 0000000..706fc66
--- /dev/null
+++ b/sound/soc/codecs/pm4125.c
@@ -0,0 +1,1780 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright (c) 2025, Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "pm4125.h"
+#include "wcd-mbhc-v2.h"
+
+#define WCD_MBHC_HS_V_MAX		1600
+#define PM4125_MBHC_MAX_BUTTONS		8
+
+#define PM4125_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+		      SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+		      SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+		      SNDRV_PCM_RATE_384000)
+
+/* Fractional Rates */
+#define PM4125_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+			   SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define PM4125_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+			SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Registers in SPMI addr space */
+#define PM4125_CODEC_RESET_REG		0xF3DB
+#define PM4125_CODEC_OFF		0x1
+#define PM4125_CODEC_ON			0x0
+#define PM4125_CODEC_FOUNDRY_ID_REG	0x7
+
+enum {
+	HPH_COMP_DELAY,
+	HPH_PA_DELAY,
+	AMIC2_BCS_ENABLE,
+};
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	NUM_CODEC_DAIS,
+};
+
+struct pm4125_priv {
+	struct sdw_slave *tx_sdw_dev;
+	struct pm4125_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+	struct device *txdev;
+	struct device *rxdev;
+	struct device_node *rxnode;
+	struct device_node *txnode;
+	struct regmap *regmap;
+	struct regmap *spmi_regmap;
+	/* mbhc module */
+	struct wcd_mbhc *wcd_mbhc;
+	struct wcd_mbhc_config mbhc_cfg;
+	struct wcd_mbhc_intr intr_ids;
+	struct irq_domain *virq;
+	const struct regmap_irq_chip *pm4125_regmap_irq_chip;
+	struct regmap_irq_chip_data *irq_chip;
+	struct snd_soc_jack *jack;
+	unsigned long status_mask;
+	s32 micb_ref[PM4125_MAX_MICBIAS];
+	s32 pullup_ref[PM4125_MAX_MICBIAS];
+	u32 micb1_mv;
+	u32 micb2_mv;
+	u32 micb3_mv;
+
+	int hphr_pdm_wd_int;
+	int hphl_pdm_wd_int;
+	bool comp1_enable;
+	bool comp2_enable;
+
+	atomic_t gloal_mbias_cnt;
+};
+
+static const char * const pm4125_power_supplies[] = {
+	"vdd-io", "vdd-cp", "vdd-mic-bias", "vdd-pa-vpos",
+};
+
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static const struct wcd_mbhc_field pm4125_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+	WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, PM4125_ANA_MBHC_MECH, 0x80),
+	WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, PM4125_ANA_MBHC_MECH, 0x40),
+	WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, PM4125_ANA_MBHC_MECH, 0x20),
+	WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x30),
+	WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, PM4125_ANA_MBHC_ELECT, 0x08),
+	WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x1F),
+	WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, PM4125_ANA_MBHC_MECH, 0x04),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x10),
+	WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x08),
+	WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, PM4125_ANA_MBHC_MECH, 0x01),
+	WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, PM4125_ANA_MBHC_ELECT, 0x06),
+	WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, PM4125_ANA_MBHC_ELECT, 0x80),
+	WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x0F),
+	WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, PM4125_ANA_MBHC_CTL_1, 0x03),
+	WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, PM4125_ANA_MBHC_CTL_2, 0x03),
+	WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x08),
+	WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+	WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x20),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x80),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x40),
+	WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x07),
+	WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, PM4125_ANA_MBHC_ELECT, 0x70),
+	WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0xFF),
+	WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0xC0),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x40),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x80),
+	WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0xC0),
+	WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+	WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x01),
+	WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, PM4125_ANA_MBHC_CTL_2, 0x70),
+	WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x20),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x80),
+	WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x20),
+	WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, PM4125_ANA_MBHC_CTL_1, 0x08),
+	WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, PM4125_ANA_MBHC_FSM_STATUS, 0x40),
+	WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, PM4125_ANA_MBHC_FSM_STATUS, 0x80),
+	WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, PM4125_ANA_MBHC_ADC_RESULT, 0xFF),
+	WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x3F),
+	WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, PM4125_ANA_MBHC_CTL_1, 0x10),
+	WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, PM4125_ANA_MBHC_CTL_1, 0x04),
+	WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, PM4125_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq pm4125_irqs[PM4125_NUM_IRQS] = {
+	REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)),
+	REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)),
+	REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)),
+	REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)),
+	REGMAP_IRQ_REG(PM4125_IRQ_MBHC_SW_DET, 0, BIT(4)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHR_OCP_INT, 0, BIT(5)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHR_CNP_INT, 0, BIT(6)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHL_OCP_INT, 0, BIT(7)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHL_CNP_INT, 1, BIT(0)),
+	REGMAP_IRQ_REG(PM4125_IRQ_EAR_CNP_INT, 1, BIT(1)),
+	REGMAP_IRQ_REG(PM4125_IRQ_EAR_SCD_INT, 1, BIT(2)),
+	REGMAP_IRQ_REG(PM4125_IRQ_AUX_CNP_INT, 1, BIT(3)),
+	REGMAP_IRQ_REG(PM4125_IRQ_AUX_SCD_INT, 1, BIT(4)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)),
+	REGMAP_IRQ_REG(PM4125_IRQ_AUX_PDM_WD_INT, 1, BIT(7)),
+	REGMAP_IRQ_REG(PM4125_IRQ_LDORT_SCD_INT, 2, BIT(0)),
+	REGMAP_IRQ_REG(PM4125_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)),
+	REGMAP_IRQ_REG(PM4125_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)),
+};
+
+static int pm4125_handle_post_irq(void *data)
+{
+	struct pm4125_priv *pm4125 = (struct pm4125_priv *)data;
+
+	regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_0, 0);
+	regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_1, 0);
+	regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_2, 0);
+
+	return IRQ_HANDLED;
+}
+
+static const u32 pm4125_config_regs[] = {
+	PM4125_DIG_SWR_INTR_LEVEL_0,
+};
+
+static struct regmap_irq_chip pm4125_regmap_irq_chip = {
+	.name = "pm4125",
+	.irqs = pm4125_irqs,
+	.num_irqs = ARRAY_SIZE(pm4125_irqs),
+	.num_regs = 3,
+	.status_base = PM4125_DIG_SWR_INTR_STATUS_0,
+	.mask_base = PM4125_DIG_SWR_INTR_MASK_0,
+	.ack_base = PM4125_DIG_SWR_INTR_CLEAR_0,
+	.use_ack = 1,
+	.clear_ack = 1,
+	.config_base = pm4125_config_regs,
+	.num_config_bases = ARRAY_SIZE(pm4125_config_regs),
+	.num_config_regs = 1,
+	.runtime_pm = true,
+	.handle_post_irq = pm4125_handle_post_irq,
+};
+
+static void pm4125_reset(struct pm4125_priv *pm4125)
+{
+	regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_OFF);
+	usleep_range(20, 30);
+	regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_ON);
+	usleep_range(5000, 5010);
+}
+
+static void pm4125_io_init(struct regmap *regmap)
+{
+	/* Disable HPH OCP */
+	regmap_update_bits(regmap, PM4125_ANA_HPHPA_CNP_CTL_2,
+			   PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK | PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK,
+			   PM4125_ANA_HPHPA_CNP_OCP_DISABLE);
+
+	/* Enable surge protection */
+	regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+			   FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+				      PM4125_ANA_SURGE_PROTECTION_ENABLE));
+	regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+			   FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+				      PM4125_ANA_SURGE_PROTECTION_ENABLE));
+
+	/* Disable mic bias 2 pull down */
+	regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN,
+			   PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+			   FIELD_PREP(PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+				      PM4125_ANA_MICBIAS_MICB_PULL_DISABLE));
+}
+
+static int pm4125_global_mbias_disable(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	if (atomic_dec_and_test(&pm4125->gloal_mbias_cnt)) {
+
+		snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+					      PM4125_ANA_MBIAS_EN_V2I_MASK,
+					      PM4125_ANA_MBIAS_EN_DISABLE);
+		snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+					      PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+					      PM4125_ANA_MBIAS_EN_DISABLE);
+	}
+
+	return 0;
+}
+
+static int pm4125_global_mbias_enable(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	if (atomic_inc_return(&pm4125->gloal_mbias_cnt) == 1) {
+		snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+					      PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+					      PM4125_ANA_MBIAS_EN_ENABLE);
+		snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+					      PM4125_ANA_MBIAS_EN_V2I_MASK,
+					      PM4125_ANA_MBIAS_EN_ENABLE);
+		usleep_range(1000, 1100);
+	}
+
+	return 0;
+}
+
+static int pm4125_rx_clk_enable(struct snd_soc_component *component)
+{
+	pm4125_global_mbias_enable(component);
+
+	snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+				      PM4125_DIG_SWR_RX_CLK_ENABLE);
+	snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+				      PM4125_DIG_SWR_RX_CLK_ENABLE);
+	usleep_range(5000, 5100);
+
+	snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+				      PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+				      PM4125_ANA_HPHPA_FSM_DIV_RATIO_68);
+	snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+				      PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+				      PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE);
+	snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL, 0x07, 0x06);
+	snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+				      PM4125_ANA_NCP_ENABLE_MASK,
+				      PM4125_ANA_NCP_ENABLE);
+	usleep_range(500, 510);
+
+	return 0;
+}
+
+static int pm4125_rx_clk_disable(struct snd_soc_component *component)
+{
+
+	snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+				      PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+				      PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE);
+	snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+				      PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+				      0x00);
+	snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+				      PM4125_ANA_NCP_ENABLE_MASK,
+				      PM4125_ANA_NCP_DISABLE);
+	snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+				      PM4125_DIG_SWR_RX_CLK_DISABLE);
+	snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+				      PM4125_DIG_SWR_RX_CLK_DISABLE);
+
+	pm4125_global_mbias_disable(component);
+
+	return 0;
+}
+
+
+static int pm4125_codec_enable_rxclk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		pm4125_rx_clk_enable(component);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		pm4125_rx_clk_disable(component);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+					      PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+					      PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+		snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+					      PM4125_SWR_HPHPA_HD2_LEFT_MASK,
+					      PM4125_SWR_HPHPA_HD2_ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (pm4125->comp1_enable) {
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						      PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+						      PM4125_DIG_SWR_COMP_ENABLE);
+
+			if (pm4125->comp2_enable)
+				snd_soc_component_write_field(component,
+							      PM4125_DIG_SWR_CDC_COMP_CTL_0,
+							      PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+							      PM4125_DIG_SWR_COMP_ENABLE);
+			/*
+			 * 5ms sleep is required after COMP is enabled as per
+			 * HW requirement
+			 */
+			usleep_range(5000, 5100);
+		} else {
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						      PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+						      PM4125_DIG_SWR_COMP_DISABLE);
+		}
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+					      PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+					      PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      PM4125_DIG_SWR_RX0_EN_MASK,
+					      PM4125_DIG_SWR_RX_INPUT_ENABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+					      PM4125_DIG_SWR_RX_CLK_ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+					      PM4125_DIG_SWR_RX_CLK_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      PM4125_DIG_SWR_RX0_EN_MASK,
+					      PM4125_DIG_SWR_RX_INPUT_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+					      PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+					      PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+		if (pm4125->comp1_enable)
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						      PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+						      PM4125_DIG_SWR_COMP_DISABLE);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+					      PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+					      PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+		snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+					      PM4125_SWR_HPHPA_HD2_RIGHT_MASK,
+					      PM4125_SWR_HPHPA_HD2_ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (pm4125->comp2_enable) {
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						      PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+						      PM4125_DIG_SWR_COMP_ENABLE);
+			if (pm4125->comp1_enable)
+				snd_soc_component_write_field(component,
+							      PM4125_DIG_SWR_CDC_COMP_CTL_0,
+							      PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+							      PM4125_DIG_SWR_COMP_ENABLE);
+			/*
+			 * 5ms sleep is required after COMP is enabled
+			 * as per HW requirement
+			 */
+			usleep_range(5000, 5100);
+		} else {
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						      PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+						      PM4125_DIG_SWR_COMP_DISABLE);
+		}
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+					      PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+					      PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      PM4125_DIG_SWR_RX1_EN_MASK,
+					      PM4125_DIG_SWR_RX_INPUT_ENABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+					      PM4125_DIG_SWR_RX_CLK_ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+					      PM4125_DIG_SWR_RX_CLK_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      PM4125_DIG_SWR_RX1_EN_MASK,
+					      PM4125_DIG_SWR_RX_INPUT_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+					      PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+					      PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+		if (pm4125->comp2_enable)
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						      PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+						      PM4125_DIG_SWR_COMP_DISABLE);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_ear_lo_dac_event(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+					      PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+					      PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+					      PM4125_DIG_SWR_RX_CLK_ENABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      PM4125_DIG_SWR_RX0_EN_MASK,
+					      PM4125_DIG_SWR_RX_INPUT_ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+					      PM4125_DIG_SWR_RX_CLK_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      PM4125_DIG_SWR_RX0_EN_MASK,
+					      PM4125_DIG_SWR_RX_INPUT_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+					      PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+					      PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int pm4125_codec_enable_hphl_wdt_irq(struct snd_soc_dapm_widget *w,
+					    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5100);
+		enable_irq(pm4125->hphl_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_hphr_wdt_irq(struct snd_soc_dapm_widget *w,
+					    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5100);
+		enable_irq(pm4125->hphr_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		usleep_range(200, 210);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+					      PM4125_WDT_ENABLE_MASK,
+					      (PM4125_WDT_ENABLE_RX1_M | PM4125_WDT_ENABLE_RX1_L));
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5100);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+					      PM4125_WDT_ENABLE_MASK, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		usleep_range(200, 210);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+					      PM4125_WDT_ENABLE_MASK,
+					      (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5100);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+					      PM4125_WDT_ENABLE_MASK, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_lo_pa(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+		usleep_range(1000, 1010);
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+		usleep_range(1000, 1010);
+		snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+					      PM4125_ANA_COMBO_PA_SELECT_MASK,
+					      PM4125_ANA_COMBO_PA_SELECT_LO);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+					      PM4125_WDT_ENABLE_MASK,
+					      (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5010);
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(2000, 2010);
+		snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+					      PM4125_ANA_COMBO_PA_SELECT_MASK,
+					      PM4125_ANA_COMBO_PA_SELECT_EAR);
+		usleep_range(5000, 5100);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+					      PM4125_WDT_ENABLE_MASK, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+		usleep_range(1000, 1010);
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+		usleep_range(1000, 1010);
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL,
+					      PM4125_ANA_COMBO_PA_SELECT_MASK,
+					      PM4125_ANA_COMBO_PA_SELECT_EAR);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+					      PM4125_WDT_ENABLE_MASK,
+					      (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5010);
+		snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5010);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+					      PM4125_WDT_ENABLE_MASK, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
+{
+	if (micb_mv < 1600 || micb_mv > 2850) {
+		dev_err(dev, "%s: unsupported micbias voltage (%u mV)\n", __func__, micb_mv);
+		return -EINVAL;
+	}
+
+	return (micb_mv - 1600) / 50;
+}
+
+static int pm4125_codec_enable_adc(struct snd_soc_dapm_widget *w,
+				   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Enable BCS for Headset mic */
+		if (w->shift == 1 &&
+			!(snd_soc_component_read(component, PM4125_ANA_TX_AMIC2) & 0x10)) {
+			set_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+		}
+		pm4125_global_mbias_enable(component);
+		if (w->shift)
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+						      PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+						      PM4125_DIG_SWR_TXD_MODE_NORMAL);
+		else
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+						      PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+						      PM4125_DIG_SWR_TXD_MODE_NORMAL);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask))
+			clear_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+
+		if (w->shift)
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+						      PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+						      0x00);
+		else
+			snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+						      PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+						      0x00);
+		pm4125_global_mbias_disable(component);
+		break;
+	};
+
+	return 0;
+}
+
+static int pm4125_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	u16 dmic_clk_reg = w->reg;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+					      PM4125_DIG_SWR_AMIC_SELECT_MASK,
+					      PM4125_DIG_SWR_AMIC_SELECT_DMIC1);
+		snd_soc_component_update_bits(component, dmic_clk_reg,
+					      PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+					      PM4125_DIG_SWR_DMIC1_CLK_ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component, dmic_clk_reg,
+					      PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+					      PM4125_DIG_SWR_DMIC1_CLK_DISABLE);
+		snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+					      PM4125_DIG_SWR_AMIC_SELECT_MASK,
+					      PM4125_DIG_SWR_AMIC_SELECT_AMIC3);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_micbias_control(struct snd_soc_component *component, int micb_num, int req,
+				  bool is_dapm)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+	int micb_index = micb_num - 1;
+	u16 micb_reg;
+	u8 pullup_mask = 0, enable_mask = 0;
+
+	if ((micb_index < 0) || (micb_index > PM4125_MAX_MICBIAS - 1)) {
+		dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+			__func__, micb_index);
+		return -EINVAL;
+	}
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+		pullup_mask = PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK;
+		enable_mask = 0x40;
+		break;
+	case MIC_BIAS_2:
+		micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+		pullup_mask = PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK;
+		enable_mask = 0x04;
+		break;
+	case MIC_BIAS_3:
+		micb_reg = PM4125_ANA_MICBIAS_MICB_3_EN;
+		pullup_mask = 0x02;
+		break;
+	default:
+		dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+			__func__, micb_num);
+		return -EINVAL;
+	};
+
+	switch (req) {
+	case MICB_PULLUP_ENABLE:
+		pm4125->pullup_ref[micb_index]++;
+		if ((pm4125->pullup_ref[micb_index] == 1) &&
+		    (pm4125->micb_ref[micb_index] == 0))
+			snd_soc_component_update_bits(component, micb_reg,
+						      pullup_mask, pullup_mask);
+		break;
+	case MICB_PULLUP_DISABLE:
+		if (pm4125->pullup_ref[micb_index] > 0)
+			pm4125->pullup_ref[micb_index]--;
+		if ((pm4125->pullup_ref[micb_index] == 0) &&
+		    (pm4125->micb_ref[micb_index] == 0))
+			snd_soc_component_update_bits(component, micb_reg,
+						      pullup_mask, 0x00);
+		break;
+	case MICB_ENABLE:
+		pm4125->micb_ref[micb_index]++;
+		if (pm4125->micb_ref[micb_index] == 1) {
+			pm4125_global_mbias_enable(component);
+			snd_soc_component_update_bits(component, micb_reg,
+						      enable_mask, enable_mask);
+		}
+		break;
+	case MICB_DISABLE:
+		if (pm4125->micb_ref[micb_index] > 0)
+			pm4125->micb_ref[micb_index]--;
+		if ((pm4125->micb_ref[micb_index] == 0) &&
+		    (pm4125->pullup_ref[micb_index] > 0)) {
+			snd_soc_component_update_bits(component, micb_reg,
+						      pullup_mask, pullup_mask);
+			snd_soc_component_update_bits(component, micb_reg,
+						      enable_mask, 0x00);
+			pm4125_global_mbias_disable(component);
+		} else if ((pm4125->micb_ref[micb_index] == 0) &&
+			   (pm4125->pullup_ref[micb_index] == 0)) {
+			snd_soc_component_update_bits(component, micb_reg,
+						      enable_mask, 0x00);
+			pm4125_global_mbias_disable(component);
+		}
+		break;
+	};
+
+	return 0;
+}
+
+static int pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+				       int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	int micb_num = w->shift;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (micb_num == MIC_BIAS_3)
+			pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+		else
+			pm4125_micbias_control(component, micb_num, MICB_ENABLE, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(1000, 1100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (micb_num == MIC_BIAS_3)
+			pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+		else
+			pm4125_micbias_control(component, micb_num, MICB_DISABLE, true);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+					      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	int micb_num = w->shift;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(1000, 1100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_connect_port(struct pm4125_sdw_priv *sdw_priv, u8 port_idx, u8 ch_id, bool enable)
+{
+	struct sdw_port_config *port_config = &sdw_priv->port_config[port_idx - 1];
+	const struct pm4125_sdw_ch_info *ch_info = &sdw_priv->ch_info[ch_id];
+	struct sdw_slave *sdev = sdw_priv->sdev;
+	u8 port_num = ch_info->port_num;
+	u8 ch_mask = ch_info->ch_mask;
+	u8 mstr_port_num, mstr_ch_mask;
+
+	port_config->num = port_num;
+
+	mstr_port_num = sdev->m_port_map[port_num];
+	mstr_ch_mask = ch_info->master_ch_mask;
+
+	if (enable) {
+		port_config->ch_mask |= ch_mask;
+		sdw_priv->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+	} else {
+		port_config->ch_mask &= ~ch_mask;
+		sdw_priv->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+	}
+
+	return 0;
+}
+
+static int pm4125_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+	struct soc_mixer_control *mc;
+	bool hphr;
+
+	mc = (struct soc_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+
+	ucontrol->value.integer.value[0] = hphr ? pm4125->comp2_enable : pm4125->comp1_enable;
+	return 0;
+}
+
+static int pm4125_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+	struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[AIF1_PB];
+	int value = ucontrol->value.integer.value[0];
+	struct soc_mixer_control *mc;
+	int portidx;
+	bool hphr;
+
+	mc = (struct soc_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+
+	if (hphr) {
+		if (value == pm4125->comp2_enable)
+			return 0;
+
+		pm4125->comp2_enable = value;
+	} else {
+		if (value == pm4125->comp1_enable)
+			return 0;
+
+		pm4125->comp1_enable = value;
+	}
+
+	portidx = sdw_priv->ch_info[mc->reg].port_num;
+
+	pm4125_connect_port(sdw_priv, portidx, mc->reg, value ? true : false);
+
+	return 1;
+}
+
+static int pm4125_get_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+	struct pm4125_sdw_priv *sdw_priv;
+	int dai_id = mixer->shift;
+	int ch_idx = mixer->reg;
+	int portidx;
+
+	sdw_priv = pm4125->sdw_priv[dai_id];
+	portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+	ucontrol->value.integer.value[0] = sdw_priv->port_enable[portidx];
+
+	return 0;
+}
+
+static int pm4125_set_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+	struct pm4125_sdw_priv *sdw_priv;
+	int dai_id = mixer->shift;
+	int ch_idx = mixer->reg;
+	int portidx;
+	bool enable;
+
+	sdw_priv = pm4125->sdw_priv[dai_id];
+
+	portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+	enable = ucontrol->value.integer.value[0];
+
+	if (enable == sdw_priv->port_enable[portidx]) {
+		pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+		return 0;
+	}
+
+	sdw_priv->port_enable[portidx] = enable;
+	pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+
+	return 1;
+}
+
+static void pm4125_mbhc_bias_control(struct snd_soc_component *component, bool enable)
+{
+	snd_soc_component_write_field(component, PM4125_ANA_MBHC_ELECT,
+				      PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK,
+				      enable ? PM4125_ANA_MBHC_ELECT_BIAS_ENABLE :
+					       PM4125_ANA_MBHC_ELECT_BIAS_DISABLE);
+}
+
+static void pm4125_mbhc_program_btn_thr(struct snd_soc_component *component,
+					int *btn_low, int *btn_high,
+					int num_btn, bool is_micbias)
+{
+	int i, vth;
+
+	if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+		dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+			__func__, num_btn);
+		return;
+	}
+
+	for (i = 0; i < num_btn; i++) {
+		vth = ((btn_high[i] * 2) / 25) & 0x3F;
+		snd_soc_component_write_field(component, PM4125_ANA_MBHC_BTN0_ZDET_VREF1 + i,
+					      PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK, vth << 2);
+	}
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+	.mbhc_bias = pm4125_mbhc_bias_control,
+	.set_btn_thr = pm4125_mbhc_program_btn_thr,
+};
+
+static int pm4125_mbhc_init(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+	struct wcd_mbhc_intr *intr_ids = &pm4125->intr_ids;
+
+	intr_ids->mbhc_sw_intr = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_MBHC_SW_DET);
+
+	intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(pm4125->irq_chip,
+							    PM4125_IRQ_MBHC_BUTTON_PRESS_DET);
+
+	intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(pm4125->irq_chip,
+							      PM4125_IRQ_MBHC_BUTTON_RELEASE_DET);
+
+	intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(pm4125->irq_chip,
+							 PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+
+	intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(pm4125->irq_chip,
+							 PM4125_IRQ_MBHC_ELECT_INS_REM_DET);
+
+	intr_ids->hph_left_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_OCP_INT);
+
+	intr_ids->hph_right_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_OCP_INT);
+
+	pm4125->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, pm4125_mbhc_fields, false);
+	if (IS_ERR(pm4125->wcd_mbhc))
+		return PTR_ERR(pm4125->wcd_mbhc);
+
+	return 0;
+}
+
+static void pm4125_mbhc_deinit(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	wcd_mbhc_deinit(pm4125->wcd_mbhc);
+}
+
+static const struct snd_kcontrol_new pm4125_snd_controls[] = {
+	SOC_SINGLE_EXT("HPHL_COMP Switch", PM4125_COMP_L, 0, 1, 0,
+		       pm4125_get_compander, pm4125_set_compander),
+	SOC_SINGLE_EXT("HPHR_COMP Switch", PM4125_COMP_R, 1, 1, 0,
+		       pm4125_get_compander, pm4125_set_compander),
+
+	SOC_SINGLE_TLV("HPHL Volume", PM4125_ANA_HPHPA_L_GAIN, 0, 20, 1,
+		       line_gain),
+	SOC_SINGLE_TLV("HPHR Volume", PM4125_ANA_HPHPA_R_GAIN, 0, 20, 1,
+		       line_gain),
+	SOC_SINGLE_TLV("ADC1 Volume", PM4125_ANA_TX_AMIC1, 0, 8, 0,
+		       analog_gain),
+	SOC_SINGLE_TLV("ADC2 Volume", PM4125_ANA_TX_AMIC2, 0, 8, 0,
+		       analog_gain),
+
+	SOC_SINGLE_EXT("HPHL Switch", PM4125_HPH_L, 0, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+	SOC_SINGLE_EXT("HPHR Switch", PM4125_HPH_R, 0, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+
+	SOC_SINGLE_EXT("ADC1 Switch", PM4125_ADC1, 1, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+	SOC_SINGLE_EXT("ADC2 Switch", PM4125_ADC2, 1, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new lo_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc2_mux_text[] = {
+	"INP2", "INP3"
+};
+
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE(PM4125_ANA_TX_AMIC2, 4,
+							 ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_soc_dapm_widget pm4125_dapm_widgets[] = {
+	/* Input widgets */
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_INPUT("AMIC2"),
+	SND_SOC_DAPM_INPUT("AMIC3"),
+	SND_SOC_DAPM_INPUT("IN1_HPHL"),
+	SND_SOC_DAPM_INPUT("IN2_HPHR"),
+
+	/* TX widgets */
+	SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, pm4125_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+	/* TX mixers */
+	SND_SOC_DAPM_MIXER("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch)),
+	SND_SOC_DAPM_MIXER("ADC2_MIXER", SND_SOC_NOPM, 1, 0, adc2_switch, ARRAY_SIZE(adc2_switch)),
+
+	/* MIC_BIAS widgets */
+	SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, pm4125_codec_enable_micbias,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, pm4125_codec_enable_micbias,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, pm4125_codec_enable_micbias,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* RX widgets */
+	SND_SOC_DAPM_PGA_E("EAR PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+			   pm4125_codec_enable_ear_pa,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LO PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+			   pm4125_codec_enable_lo_pa,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HPHL PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL, 0,
+			   pm4125_codec_enable_hphl_pa,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HPHR PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL, 0,
+			   pm4125_codec_enable_hphr_pa,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphl_dac_event,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_hphr_dac_event,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, pm4125_codec_ear_lo_dac_event,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+
+	SND_SOC_DAPM_SUPPLY("HPHL_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphl_wdt_irq,
+			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("HPHR_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphr_wdt_irq,
+			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_rxclk,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
+
+	/* RX mixer widgets */
+	SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, ear_rdac_switch,
+			   ARRAY_SIZE(ear_rdac_switch)),
+	SND_SOC_DAPM_MIXER("LO_RDAC", SND_SOC_NOPM, 0, 0, lo_rdac_switch,
+			   ARRAY_SIZE(lo_rdac_switch)),
+	SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, hphl_rdac_switch,
+			   ARRAY_SIZE(hphl_rdac_switch)),
+	SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, hphr_rdac_switch,
+			   ARRAY_SIZE(hphr_rdac_switch)),
+
+	/* TX output widgets */
+	SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+	SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+
+	/* RX output widgets */
+	SND_SOC_DAPM_OUTPUT("EAR"),
+	SND_SOC_DAPM_OUTPUT("LO"),
+	SND_SOC_DAPM_OUTPUT("HPHL"),
+	SND_SOC_DAPM_OUTPUT("HPHR"),
+
+	/* MIC_BIAS pull up widgets */
+	SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+			    pm4125_codec_enable_micbias_pullup,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+			    pm4125_codec_enable_micbias_pullup,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+			    pm4125_codec_enable_micbias_pullup,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* TX widgets */
+	SND_SOC_DAPM_ADC_E("DMIC1", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 0, 0,
+			   pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 1, 0,
+			   pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* TX mixer widgets */
+	SND_SOC_DAPM_MIXER("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch,
+			   ARRAY_SIZE(dmic1_switch)),
+	SND_SOC_DAPM_MIXER("DMIC2_MIXER", SND_SOC_NOPM, 1, 0, dmic2_switch,
+			   ARRAY_SIZE(dmic2_switch)),
+
+	/* Output widgets */
+	SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+	SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route pm4125_audio_map[] = {
+	{ "ADC1_OUTPUT", NULL, "ADC1_MIXER" },
+	{ "ADC1_MIXER", "Switch", "ADC1" },
+	{ "ADC1", NULL, "AMIC1" },
+
+	{ "ADC2_OUTPUT", NULL, "ADC2_MIXER" },
+	{ "ADC2_MIXER", "Switch", "ADC2" },
+	{ "ADC2", NULL, "ADC2 MUX" },
+	{ "ADC2 MUX", "INP3", "AMIC3" },
+	{ "ADC2 MUX", "INP2", "AMIC2" },
+
+	{ "IN1_HPHL", NULL, "PA_VPOS" },
+	{ "RX1", NULL, "IN1_HPHL" },
+	{ "RX1", NULL, "RXCLK" },
+	{ "RX1", NULL, "HPHL_WDT_IRQ" },
+	{ "RDAC1", NULL, "RX1" },
+	{ "HPHL_RDAC", "Switch", "RDAC1" },
+	{ "HPHL PGA", NULL, "HPHL_RDAC" },
+	{ "HPHL", NULL, "HPHL PGA" },
+
+	{ "IN2_HPHR", NULL, "PA_VPOS" },
+	{ "RX2", NULL, "IN2_HPHR" },
+	{ "RX2", NULL, "RXCLK" },
+	{ "RX2", NULL, "HPHR_WDT_IRQ" },
+	{ "RDAC2", NULL, "RX2" },
+	{ "HPHR_RDAC", "Switch", "RDAC2" },
+	{ "HPHR PGA", NULL, "HPHR_RDAC" },
+	{ "HPHR", NULL, "HPHR PGA" },
+
+	{ "RDAC3", NULL, "RX1" },
+	{ "EAR_RDAC", "Switch", "RDAC3" },
+	{ "EAR PGA", NULL, "EAR_RDAC" },
+	{ "EAR", NULL, "EAR PGA" },
+
+	{ "LO_RDAC", "Switch", "RDAC3" },
+	{ "LO PGA", NULL, "LO_RDAC" },
+	{ "LO", NULL, "LO PGA" },
+
+	{ "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" },
+	{ "DMIC1_MIXER", "Switch", "DMIC1" },
+
+	{ "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" },
+	{ "DMIC2_MIXER", "Switch", "DMIC2" },
+};
+
+static int pm4125_set_micbias_data(struct device *dev, struct pm4125_priv *pm4125)
+{
+	int vout_ctl;
+
+	/* Set micbias voltage */
+	vout_ctl = pm4125_get_micb_vout_ctl_val(dev, pm4125->micb1_mv);
+	if (vout_ctl < 0)
+		return -EINVAL;
+
+	regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
+			   PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK, vout_ctl << 3);
+	return 0;
+}
+
+static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
+{
+	/*
+	 * HPHR/HPHL Watchdog interrupt threaded handler
+	 * Watchdog interrupts are expected to be enabled when switching on the HPHL/R
+	 * in order to make sure the interrupts are acked by the regmap_irq handler
+	 * io allow PDM sync. We could leave those interrupts masked but we would
+	 * not haveany valid way to enable/disable them without violating irq layers.
+	 *
+	 * The HPHR/HPHL Watchdog interrupts are handled by regmap_irq, so requesting
+	 * a threaded handler is the safest way to be able to ack those interrupts
+	 * without colliding with the regmap_irq setup.
+	 */
+	return IRQ_HANDLED;
+}
+
+static const struct irq_chip pm4125_codec_irq_chip = {
+	.name = "pm4125_codec",
+};
+
+static int pm4125_codec_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+				     irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &pm4125_codec_irq_chip, handle_simple_irq);
+	irq_set_nested_thread(virq, 1);
+	irq_set_noprobe(virq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops pm4125_domain_ops = {
+	.map = pm4125_codec_irq_chip_map,
+};
+
+static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev)
+{
+	pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL);
+	if (!(pm4125->virq)) {
+		dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+		return -EINVAL;
+	}
+
+	pm4125_regmap_irq_chip.irq_drv_data = pm4125;
+
+	return devm_regmap_add_irq_chip(dev, pm4125->regmap, irq_create_mapping(pm4125->virq, 0),
+					IRQF_ONESHOT, 0, &pm4125_regmap_irq_chip,
+					&pm4125->irq_chip);
+}
+
+static int pm4125_soc_codec_probe(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+	struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev;
+	struct device *dev = component->dev;
+	unsigned long time_left;
+	int i, ret;
+
+	time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+						msecs_to_jiffies(5000));
+	if (!time_left) {
+		dev_err(dev, "soundwire device init timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	snd_soc_component_init_regmap(component, pm4125->regmap);
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret < 0)
+		return ret;
+
+	pm4125_io_init(pm4125->regmap);
+
+	/* Set all interrupts as edge triggered */
+	for (i = 0; i < pm4125_regmap_irq_chip.num_regs; i++)
+		regmap_write(pm4125->regmap, (PM4125_DIG_SWR_INTR_LEVEL_0 + i), 0);
+
+	pm_runtime_put(dev);
+
+	pm4125->hphr_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_PDM_WD_INT);
+	pm4125->hphl_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_PDM_WD_INT);
+
+	/* Request for watchdog interrupts */
+	ret = devm_request_threaded_irq(dev, pm4125->hphr_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+					"HPHR PDM WDOG INT", pm4125);
+	if (ret)
+		dev_err(dev, "Failed to request HPHR wdt interrupt: %d\n", ret);
+
+	ret = devm_request_threaded_irq(dev, pm4125->hphl_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+					"HPHL PDM WDOG INT", pm4125);
+	if (ret)
+		dev_err(dev, "Failed to request HPHL wdt interrupt: %d\n", ret);
+
+	disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+	disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+
+	ret = pm4125_mbhc_init(component);
+	if (ret)
+		dev_err(component->dev, "mbhc initialization failed\n");
+
+	return ret;
+}
+
+static void pm4125_soc_codec_remove(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	pm4125_mbhc_deinit(component);
+	free_irq(pm4125->hphl_pdm_wd_int, pm4125);
+	free_irq(pm4125->hphr_pdm_wd_int, pm4125);
+}
+
+static int pm4125_codec_set_jack(struct snd_soc_component *comp, struct snd_soc_jack *jack,
+				 void *data)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(comp->dev);
+	int ret = 0;
+
+	if (jack)
+		ret = wcd_mbhc_start(pm4125->wcd_mbhc, &pm4125->mbhc_cfg, jack);
+	else
+		wcd_mbhc_stop(pm4125->wcd_mbhc);
+
+	return ret;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_pm4125 = {
+	.name = "pm4125_codec",
+	.probe = pm4125_soc_codec_probe,
+	.remove = pm4125_soc_codec_remove,
+	.controls = pm4125_snd_controls,
+	.num_controls = ARRAY_SIZE(pm4125_snd_controls),
+	.dapm_widgets = pm4125_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(pm4125_dapm_widgets),
+	.dapm_routes = pm4125_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(pm4125_audio_map),
+	.set_jack = pm4125_codec_set_jack,
+	.endianness = 1,
+};
+
+static void pm4125_dt_parse_micbias_info(struct device *dev, struct pm4125_priv *priv)
+{
+	struct device_node *np = dev->of_node;
+	u32 prop_val = 0;
+	int ret;
+
+	ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
+	if (!ret)
+		priv->micb1_mv = prop_val / 1000;
+	else
+		dev_warn(dev, "Micbias1 DT property not found\n");
+
+	ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
+	if (!ret)
+		priv->micb2_mv = prop_val / 1000;
+	else
+		dev_warn(dev, "Micbias2 DT property not found\n");
+
+	ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
+	if (!ret)
+		priv->micb3_mv = prop_val / 1000;
+	else
+		dev_warn(dev, "Micbias3 DT property not found\n");
+}
+
+static int pm4125_codec_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+	struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+	return pm4125_sdw_hw_params(sdw_priv, substream, params, dai);
+}
+
+static int pm4125_codec_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+	struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+	return sdw_stream_remove_slave(sdw_priv->sdev, sdw_priv->sruntime);
+}
+
+static int pm4125_codec_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+	struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+	sdw_priv->sruntime = stream;
+
+	return 0;
+}
+
+static int pm4125_get_channel_map(const struct snd_soc_dai *dai,
+				  unsigned int *tx_num, unsigned int *tx_slot,
+				  unsigned int *rx_num, unsigned int *rx_slot)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+	struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+	int i;
+
+	switch (dai->id) {
+	case AIF1_PB:
+		if (!rx_slot || !rx_num) {
+			dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", rx_slot, rx_num);
+			return -EINVAL;
+		}
+
+		for (i = 0; i < SDW_MAX_PORTS; i++)
+			rx_slot[i] = sdw_priv->master_channel_map[i];
+
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+		if (!tx_slot || !tx_num) {
+			dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", tx_slot, tx_num);
+			return -EINVAL;
+		}
+
+		for (i = 0; i < SDW_MAX_PORTS; i++)
+			tx_slot[i] = sdw_priv->master_channel_map[i];
+
+		*tx_num = i;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops pm4125_sdw_dai_ops = {
+	.hw_params = pm4125_codec_hw_params,
+	.hw_free = pm4125_codec_free,
+	.set_stream = pm4125_codec_set_sdw_stream,
+	.get_channel_map = pm4125_get_channel_map,
+};
+
+static struct snd_soc_dai_driver pm4125_dais[] = {
+	[0] = {
+		.name = "pm4125-sdw-rx",
+		.playback = {
+			.stream_name = "PM4125 AIF Playback",
+			.rates = PM4125_RATES | PM4125_FRAC_RATES,
+			.formats = PM4125_FORMATS,
+			.rate_min = 8000,
+			.rate_max = 384000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &pm4125_sdw_dai_ops,
+	},
+	[1] = {
+		.name = "pm4125-sdw-tx",
+		.capture = {
+			.stream_name = "PM4125 AIF Capture",
+			.rates = PM4125_RATES,
+			.formats = PM4125_FORMATS,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &pm4125_sdw_dai_ops,
+	},
+};
+
+static int pm4125_bind(struct device *dev)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+	struct device_link *devlink;
+	int ret;
+
+	/* Give the soundwire subdevices some more time to settle */
+	usleep_range(15000, 15010);
+
+	ret = component_bind_all(dev, pm4125);
+	if (ret) {
+		dev_err(dev, "Slave bind failed, ret = %d\n", ret);
+		return ret;
+	}
+
+	pm4125->rxdev = pm4125_sdw_device_get(pm4125->rxnode);
+	if (!pm4125->rxdev) {
+		dev_err(dev, "could not find rxslave with matching of node\n");
+		ret = -EINVAL;
+		goto error_unbind_all;
+	}
+
+	pm4125->sdw_priv[AIF1_PB] = dev_get_drvdata(pm4125->rxdev);
+	pm4125->sdw_priv[AIF1_PB]->pm4125 = pm4125;
+
+	pm4125->txdev = pm4125_sdw_device_get(pm4125->txnode);
+	if (!pm4125->txdev) {
+		dev_err(dev, "could not find txslave with matching of node\n");
+		ret = -EINVAL;
+		goto error_unbind_all;
+	}
+
+	pm4125->sdw_priv[AIF1_CAP] = dev_get_drvdata(pm4125->txdev);
+	pm4125->sdw_priv[AIF1_CAP]->pm4125 = pm4125;
+
+	pm4125->tx_sdw_dev = dev_to_sdw_dev(pm4125->txdev);
+	if (!pm4125->tx_sdw_dev) {
+		dev_err(dev, "could not get txslave with matching of dev\n");
+		ret = -EINVAL;
+		goto error_unbind_all;
+	}
+
+	/*
+	 * As TX is the main CSR reg interface, which should not be suspended first.
+	 * expicilty add the dependency link
+	 */
+	devlink = device_link_add(pm4125->rxdev, pm4125->txdev,
+				  DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+	if (!devlink) {
+		dev_err(dev, "Could not devlink TX and RX\n");
+		ret = -EINVAL;
+		goto error_unbind_all;
+	}
+
+	devlink = device_link_add(dev, pm4125->txdev,
+				  DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+	if (!devlink) {
+		dev_err(dev, "Could not devlink PM4125 and TX\n");
+		ret = -EINVAL;
+		goto link_remove_rx_tx;
+	}
+
+	devlink = device_link_add(dev, pm4125->rxdev,
+				  DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+	if (!devlink) {
+		dev_err(dev, "Could not devlink PM4125 and RX\n");
+		ret = -EINVAL;
+		goto link_remove_dev_tx;
+	}
+
+	pm4125->regmap = dev_get_regmap(&pm4125->tx_sdw_dev->dev, NULL);
+	if (!pm4125->regmap) {
+		dev_err(dev, "could not get TX device regmap\n");
+		ret = -EINVAL;
+		goto link_remove_dev_rx;
+	}
+
+	ret = pm4125_irq_init(pm4125, dev);
+	if (ret) {
+		dev_err(dev, "IRQ init failed: %d\n", ret);
+		goto link_remove_dev_rx;
+	}
+
+	pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
+	pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
+
+	ret = pm4125_set_micbias_data(dev, pm4125);
+	if (ret < 0) {
+		dev_err(dev, "Bad micbias pdata\n");
+		goto link_remove_dev_rx;
+	}
+
+	ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
+					 pm4125_dais, ARRAY_SIZE(pm4125_dais));
+	if (!ret)
+		return ret;
+
+	dev_err(dev, "Codec registration failed\n");
+
+link_remove_dev_rx:
+	device_link_remove(dev, pm4125->rxdev);
+link_remove_dev_tx:
+	device_link_remove(dev, pm4125->txdev);
+link_remove_rx_tx:
+	device_link_remove(pm4125->rxdev, pm4125->txdev);
+error_unbind_all:
+	component_unbind_all(dev, pm4125);
+	return ret;
+}
+
+static void pm4125_unbind(struct device *dev)
+{
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+
+	snd_soc_unregister_component(dev);
+	device_link_remove(dev, pm4125->txdev);
+	device_link_remove(dev, pm4125->rxdev);
+	device_link_remove(pm4125->rxdev, pm4125->txdev);
+	component_unbind_all(dev, pm4125);
+}
+
+static const struct component_master_ops pm4125_comp_ops = {
+	.bind = pm4125_bind,
+	.unbind = pm4125_unbind,
+};
+
+static int pm4125_add_slave_components(struct pm4125_priv *pm4125, struct device *dev,
+				       struct component_match **matchptr)
+{
+	struct device_node *np = dev->of_node;
+
+	pm4125->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+	if (!pm4125->rxnode)
+		return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,rx-device\n");
+	component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+				    pm4125->rxnode);
+
+	pm4125->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+	if (!pm4125->txnode)
+		return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,tx-device\n");
+	component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+				    pm4125->txnode);
+
+	return 0;
+}
+
+static int pm4125_probe(struct platform_device *pdev)
+{
+	struct component_match *match = NULL;
+	struct device *dev = &pdev->dev;
+	struct pm4125_priv *pm4125;
+	struct wcd_mbhc_config *cfg;
+	int ret;
+
+	pm4125 = devm_kzalloc(dev, sizeof(*pm4125), GFP_KERNEL);
+	if (!pm4125)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, pm4125);
+
+	ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(pm4125_power_supplies),
+					     pm4125_power_supplies);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+	pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pm4125->spmi_regmap)
+		return -ENXIO;
+
+	pm4125_reset(pm4125);
+
+	pm4125_dt_parse_micbias_info(dev, pm4125);
+	atomic_set(&pm4125->gloal_mbias_cnt, 0);
+
+	cfg = &pm4125->mbhc_cfg;
+	cfg->mbhc_micbias = MIC_BIAS_2;
+	cfg->anc_micbias = MIC_BIAS_2;
+	cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+	cfg->num_btn = PM4125_MBHC_MAX_BUTTONS;
+	cfg->micb_mv = pm4125->micb2_mv;
+	cfg->linein_th = 5000;
+	cfg->hs_thr = 1700;
+	cfg->hph_thr = 50;
+
+	wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
+
+	ret = pm4125_add_slave_components(pm4125, dev, &match);
+	if (ret)
+		return ret;
+
+	ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
+	if (ret)
+		return ret;
+
+	pm_runtime_set_autosuspend_delay(dev, 1000);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+}
+
+static void pm4125_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	component_master_del(&pdev->dev, &pm4125_comp_ops);
+
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct of_device_id pm4125_of_match[] = {
+	{ .compatible = "qcom,pm4125-codec" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm4125_of_match);
+
+static struct platform_driver pm4125_codec_driver = {
+	.probe = pm4125_probe,
+	.remove = pm4125_remove,
+	.driver = {
+		.name = "pm4125_codec",
+		.of_match_table = pm4125_of_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
+module_platform_driver(pm4125_codec_driver);
+MODULE_DESCRIPTION("PM4125 audio codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.h b/sound/soc/codecs/pm4125.h
new file mode 100644
index 0000000..3520c71
--- /dev/null
+++ b/sound/soc/codecs/pm4125.h
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _PM4125_REGISTERS_H
+#define _PM4125_REGISTERS_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define PM4125_ANA_BASE_ADDR			0x3000
+#define PM4125_DIG_BASE_ADDR			0x3400
+
+#define PM4125_ANA_MICBIAS_MICB_1_2_EN		(PM4125_ANA_BASE_ADDR+0x040)
+#define  PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK	BIT(5)
+#define  PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK	BIT(1)
+#define  PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK	BIT(0)
+#define  PM4125_ANA_MICBIAS_MICB_PULL_ENABLE	1
+#define  PM4125_ANA_MICBIAS_MICB_PULL_DISABLE	0
+#define PM4125_ANA_MICBIAS_MICB_3_EN		(PM4125_ANA_BASE_ADDR+0x041)
+#define PM4125_ANA_MICBIAS_LDO_1_SETTING	(PM4125_ANA_BASE_ADDR+0x042)
+#define  PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK	GENMASK(7, 3)
+#define PM4125_ANA_MICBIAS_LDO_1_CTRL		(PM4125_ANA_BASE_ADDR+0x043)
+#define PM4125_ANA_TX_AMIC1			(PM4125_ANA_BASE_ADDR+0x047)
+#define PM4125_ANA_TX_AMIC2			(PM4125_ANA_BASE_ADDR+0x048)
+#define PM4125_ANA_MBHC_MECH			(PM4125_ANA_BASE_ADDR+0x05A)
+#define PM4125_ANA_MBHC_ELECT			(PM4125_ANA_BASE_ADDR+0x05B)
+#define  PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK	BIT(0)
+#define  PM4125_ANA_MBHC_ELECT_BIAS_ENABLE	1
+#define  PM4125_ANA_MBHC_ELECT_BIAS_DISABLE	0
+#define PM4125_ANA_MBHC_ZDET			(PM4125_ANA_BASE_ADDR+0x05C)
+#define PM4125_ANA_MBHC_RESULT_1		(PM4125_ANA_BASE_ADDR+0x05D)
+#define PM4125_ANA_MBHC_RESULT_2		(PM4125_ANA_BASE_ADDR+0x05E)
+#define PM4125_ANA_MBHC_RESULT_3		(PM4125_ANA_BASE_ADDR+0x05F)
+#define PM4125_ANA_MBHC_BTN0_ZDET_VREF1		(PM4125_ANA_BASE_ADDR+0x060)
+#define  PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK	GENMASK(7, 2)
+#define PM4125_ANA_MBHC_BTN1_ZDET_VREF2		(PM4125_ANA_BASE_ADDR+0x061)
+#define PM4125_ANA_MBHC_BTN2_ZDET_VREF3		(PM4125_ANA_BASE_ADDR+0x062)
+#define PM4125_ANA_MBHC_BTN3_ZDET_DBG_400	(PM4125_ANA_BASE_ADDR+0x063)
+#define PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400	(PM4125_ANA_BASE_ADDR+0x064)
+#define PM4125_ANA_MBHC_MICB2_RAMP		(PM4125_ANA_BASE_ADDR+0x065)
+#define PM4125_ANA_MBHC_CTL_1			(PM4125_ANA_BASE_ADDR+0x066)
+#define PM4125_ANA_MBHC_CTL_2			(PM4125_ANA_BASE_ADDR+0x067)
+#define PM4125_ANA_MBHC_PLUG_DETECT_CTL		(PM4125_ANA_BASE_ADDR+0x068)
+#define PM4125_ANA_MBHC_ZDET_ANA_CTL		(PM4125_ANA_BASE_ADDR+0x069)
+#define PM4125_ANA_MBHC_ZDET_RAMP_CTL		(PM4125_ANA_BASE_ADDR+0x06A)
+#define PM4125_ANA_MBHC_FSM_STATUS		(PM4125_ANA_BASE_ADDR+0x06B)
+#define PM4125_ANA_MBHC_ADC_RESULT		(PM4125_ANA_BASE_ADDR+0x06C)
+#define PM4125_ANA_MBHC_CTL_CLK			(PM4125_ANA_BASE_ADDR+0x06D)
+#define PM4125_ANA_MBHC_ZDET_CALIB_RESULT	(PM4125_ANA_BASE_ADDR+0x072)
+#define PM4125_ANA_NCP_EN			(PM4125_ANA_BASE_ADDR+0x077)
+#define  PM4125_ANA_NCP_ENABLE_MASK		BIT(0)
+#define  PM4125_ANA_NCP_ENABLE			1
+#define  PM4125_ANA_NCP_DISABLE			0
+#define PM4125_ANA_NCP_VCTRL			(PM4125_ANA_BASE_ADDR+0x07C)
+#define PM4125_ANA_HPHPA_CNP_CTL_1		(PM4125_ANA_BASE_ADDR+0x083)
+#define  PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK	BIT(1)
+#define  PM4125_ANA_HPHPA_CNP_CTL_1_EN		1
+#define PM4125_ANA_HPHPA_CNP_CTL_2		(PM4125_ANA_BASE_ADDR+0x084)
+#define  PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK	BIT(1)
+#define  PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK	BIT(0)
+#define  PM4125_ANA_HPHPA_CNP_OCP_ENABLE	1
+#define  PM4125_ANA_HPHPA_CNP_OCP_DISABLE	0
+#define PM4125_ANA_HPHPA_PA_STATUS		(PM4125_ANA_BASE_ADDR+0x087)
+#define PM4125_ANA_HPHPA_FSM_CLK		(PM4125_ANA_BASE_ADDR+0x088)
+#define  PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK	BIT(7)
+#define  PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE	1
+#define  PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE	0
+#define  PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK	GENMASK(6, 0)
+#define  PM4125_ANA_HPHPA_FSM_DIV_RATIO_68	(0x11)
+#define PM4125_ANA_HPHPA_L_GAIN			(PM4125_ANA_BASE_ADDR+0x08B)
+#define PM4125_ANA_HPHPA_R_GAIN			(PM4125_ANA_BASE_ADDR+0x08C)
+#define PM4125_ANA_HPHPA_SPARE_CTL		(PM4125_ANA_BASE_ADDR+0x08E)
+#define PM4125_SWR_HPHPA_HD2			(PM4125_ANA_BASE_ADDR+0x090)
+#define  PM4125_SWR_HPHPA_HD2_LEFT_MASK		GENMASK(5, 3)
+#define  PM4125_SWR_HPHPA_HD2_RIGHT_MASK	GENMASK(2, 0)
+#define  PM4125_SWR_HPHPA_HD2_ENABLE		(BIT(2) | BIT(1) | BIT(0))
+#define PM4125_ANA_SURGE_EN			(PM4125_ANA_BASE_ADDR+0x097)
+#define  PM4125_ANA_SURGE_PROTECTION_HPHL_MASK	BIT(7)
+#define  PM4125_ANA_SURGE_PROTECTION_HPHR_MASK	BIT(6)
+#define  PM4125_ANA_SURGE_PROTECTION_ENABLE	1
+#define  PM4125_ANA_SURGE_PROTECTION_DISABLE	0
+#define PM4125_ANA_COMBOPA_CTL			(PM4125_ANA_BASE_ADDR+0x09B)
+#define  PM4125_ANA_COMBO_PA_SELECT_MASK	BIT(6)
+#define  PM4125_ANA_COMBO_PA_SELECT_EAR		0
+#define  PM4125_ANA_COMBO_PA_SELECT_LO		1
+#define PM4125_ANA_COMBOPA_CTL_4		(PM4125_ANA_BASE_ADDR+0x09F)
+#define PM4125_ANA_COMBOPA_CTL_5		(PM4125_ANA_BASE_ADDR+0x0A0)
+#define PM4125_ANA_RXLDO_CTL			(PM4125_ANA_BASE_ADDR+0x0B2)
+#define PM4125_ANA_MBIAS_EN			(PM4125_ANA_BASE_ADDR+0x0B4)
+#define  PM4125_ANA_MBIAS_EN_GLOBAL_MASK	BIT(5)
+#define  PM4125_ANA_MBIAS_EN_V2I_MASK		BIT(4)
+#define  PM4125_ANA_MBIAS_EN_ENABLE		1
+#define  PM4125_ANA_MBIAS_EN_DISABLE		0
+
+#define PM4125_DIG_SWR_CHIP_ID0			(PM4125_DIG_BASE_ADDR+0x001)
+#define PM4125_DIG_SWR_CHIP_ID1			(PM4125_DIG_BASE_ADDR+0x002)
+#define PM4125_DIG_SWR_CHIP_ID2			(PM4125_DIG_BASE_ADDR+0x003)
+#define PM4125_DIG_SWR_CHIP_ID3			(PM4125_DIG_BASE_ADDR+0x004)
+#define PM4125_DIG_SWR_SWR_TX_CLK_RATE		(PM4125_DIG_BASE_ADDR+0x040)
+#define PM4125_DIG_SWR_CDC_RST_CTL		(PM4125_DIG_BASE_ADDR+0x041)
+#define PM4125_DIG_SWR_TOP_CLK_CFG		(PM4125_DIG_BASE_ADDR+0x042)
+#define PM4125_DIG_SWR_CDC_RX_CLK_CTL		(PM4125_DIG_BASE_ADDR+0x043)
+#define  PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK	BIT(5)
+#define  PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK	BIT(4)
+#define  PM4125_DIG_SWR_RX1_CLK_EN_MASK		BIT(1)
+#define  PM4125_DIG_SWR_RX0_CLK_EN_MASK		BIT(0)
+#define  PM4125_DIG_SWR_RX_CLK_ENABLE		1
+#define  PM4125_DIG_SWR_RX_CLK_DISABLE		0
+#define PM4125_DIG_SWR_CDC_TX_CLK_CTL		(PM4125_DIG_BASE_ADDR+0x044)
+#define PM4125_DIG_SWR_SWR_RST_EN		(PM4125_DIG_BASE_ADDR+0x045)
+#define PM4125_DIG_SWR_CDC_RX_RST		(PM4125_DIG_BASE_ADDR+0x047)
+#define PM4125_DIG_SWR_CDC_RX0_CTL		(PM4125_DIG_BASE_ADDR+0x048)
+#define  PM4125_DIG_SWR_DSM_DITHER_EN_MASK	BIT(7)
+#define  PM4125_DIG_SWR_DSM_DITHER_DISABLE	0
+#define  PM4125_DIG_SWR_DSM_DITHER_ENABLE	1
+#define PM4125_DIG_SWR_CDC_RX1_CTL		(PM4125_DIG_BASE_ADDR+0x049)
+#define PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1	(PM4125_DIG_BASE_ADDR+0x04B)
+#define  PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK	GENMASK(7, 4)
+#define  PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK	GENMASK(3, 0)
+#define  PM4125_DIG_SWR_TXD_MODE_ULPI		(0x9)
+#define  PM4125_DIG_SWR_TXD_MODE_NORMAL		(0x3)
+#define PM4125_DIG_SWR_CDC_COMP_CTL_0		(PM4125_DIG_BASE_ADDR+0x04F)
+#define  PM4125_DIG_SWR_COMP_HPHL_EN_MASK	BIT(1)
+#define  PM4125_DIG_SWR_COMP_HPHR_EN_MASK	BIT(0)
+#define  PM4125_DIG_SWR_COMP_ENABLE		1
+#define  PM4125_DIG_SWR_COMP_DISABLE		0
+#define PM4125_DIG_SWR_CDC_RX_DELAY_CTL		(PM4125_DIG_BASE_ADDR+0x052)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_0		(PM4125_DIG_BASE_ADDR+0x053)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_1		(PM4125_DIG_BASE_ADDR+0x054)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_CTL		(PM4125_DIG_BASE_ADDR+0x057)
+#define  PM4125_DIG_SWR_RX1_EN_MASK		BIT(3)
+#define  PM4125_DIG_SWR_RX0_EN_MASK		BIT(2)
+#define  PM4125_DIG_SWR_RX_INPUT_DISABLE	0
+#define  PM4125_DIG_SWR_RX_INPUT_ENABLE		1
+#define PM4125_DIG_SWR_CDC_TX0_CTL		(PM4125_DIG_BASE_ADDR+0x060)
+#define PM4125_DIG_SWR_CDC_TX1_CTL		(PM4125_DIG_BASE_ADDR+0x061)
+#define PM4125_DIG_SWR_CDC_TX_RST		(PM4125_DIG_BASE_ADDR+0x063)
+#define PM4125_DIG_SWR_CDC_REQ0_CTL		(PM4125_DIG_BASE_ADDR+0x064)
+#define PM4125_DIG_SWR_CDC_REQ1_CTL		(PM4125_DIG_BASE_ADDR+0x065)
+#define PM4125_DIG_SWR_CDC_RST			(PM4125_DIG_BASE_ADDR+0x067)
+#define PM4125_DIG_SWR_CDC_AMIC_CTL		(PM4125_DIG_BASE_ADDR+0x06A)
+#define  PM4125_DIG_SWR_AMIC_SELECT_MASK	BIT(1)
+#define  PM4125_DIG_SWR_AMIC_SELECT_DMIC1	0
+#define  PM4125_DIG_SWR_AMIC_SELECT_AMIC3	1
+#define PM4125_DIG_SWR_CDC_DMIC_CTL		(PM4125_DIG_BASE_ADDR+0x06B)
+#define PM4125_DIG_SWR_CDC_DMIC1_CTL		(PM4125_DIG_BASE_ADDR+0x06C)
+#define  PM4125_DIG_SWR_DMIC1_CLK_EN_MASK	BIT(3)
+#define  PM4125_DIG_SWR_DMIC1_CLK_ENABLE	1
+#define  PM4125_DIG_SWR_DMIC1_CLK_DISABLE	0
+#define PM4125_DIG_SWR_CDC_DMIC1_RATE		(PM4125_DIG_BASE_ADDR+0x06D)
+#define PM4125_DIG_SWR_PDM_WD_CTL0		(PM4125_DIG_BASE_ADDR+0x070)
+#define  PM4125_WDT_ENABLE_MASK			GENMASK(1, 0)
+#define  PM4125_WDT_ENABLE_RX0_L		BIT(0)
+#define  PM4125_WDT_ENABLE_RX0_M		BIT(1)
+#define PM4125_DIG_SWR_PDM_WD_CTL1		(PM4125_DIG_BASE_ADDR+0x071)
+#define  PM4125_WDT_ENABLE_RX1_L		BIT(0)
+#define  PM4125_WDT_ENABLE_RX1_M		BIT(1)
+#define PM4125_DIG_SWR_INTR_MODE		(PM4125_DIG_BASE_ADDR+0x080)
+#define PM4125_DIG_SWR_INTR_MASK_0		(PM4125_DIG_BASE_ADDR+0x081)
+#define PM4125_DIG_SWR_INTR_MASK_1		(PM4125_DIG_BASE_ADDR+0x082)
+#define PM4125_DIG_SWR_INTR_MASK_2		(PM4125_DIG_BASE_ADDR+0x083)
+#define PM4125_DIG_SWR_INTR_STATUS_0		(PM4125_DIG_BASE_ADDR+0x084)
+#define PM4125_DIG_SWR_INTR_STATUS_1		(PM4125_DIG_BASE_ADDR+0x085)
+#define PM4125_DIG_SWR_INTR_STATUS_2		(PM4125_DIG_BASE_ADDR+0x086)
+#define PM4125_DIG_SWR_INTR_CLEAR_0		(PM4125_DIG_BASE_ADDR+0x087)
+#define PM4125_DIG_SWR_INTR_CLEAR_1		(PM4125_DIG_BASE_ADDR+0x088)
+#define PM4125_DIG_SWR_INTR_CLEAR_2		(PM4125_DIG_BASE_ADDR+0x089)
+#define PM4125_DIG_SWR_INTR_LEVEL_0		(PM4125_DIG_BASE_ADDR+0x08A)
+#define PM4125_DIG_SWR_INTR_LEVEL_1		(PM4125_DIG_BASE_ADDR+0x08B)
+#define PM4125_DIG_SWR_INTR_LEVEL_2		(PM4125_DIG_BASE_ADDR+0x08C)
+#define PM4125_DIG_SWR_CDC_CONN_RX0_CTL		(PM4125_DIG_BASE_ADDR+0x093)
+#define PM4125_DIG_SWR_CDC_CONN_RX1_CTL		(PM4125_DIG_BASE_ADDR+0x094)
+#define PM4125_DIG_SWR_LOOP_BACK_MODE		(PM4125_DIG_BASE_ADDR+0x097)
+#define PM4125_DIG_SWR_DRIVE_STRENGTH_0		(PM4125_DIG_BASE_ADDR+0x0A0)
+#define PM4125_DIG_SWR_DIG_DEBUG_CTL		(PM4125_DIG_BASE_ADDR+0x0AB)
+#define PM4125_DIG_SWR_DIG_DEBUG_EN		(PM4125_DIG_BASE_ADDR+0x0AC)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA0		(PM4125_DIG_BASE_ADDR+0x0B0)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA1		(PM4125_DIG_BASE_ADDR+0x0B1)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA2		(PM4125_DIG_BASE_ADDR+0x0B2)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA3		(PM4125_DIG_BASE_ADDR+0x0B3)
+
+#define PM4125_ANALOG_REGISTERS_MAX_SIZE	(PM4125_ANA_BASE_ADDR+0x0B5)
+#define PM4125_DIGITAL_REGISTERS_MAX_SIZE	(PM4125_DIG_BASE_ADDR+0x0B4)
+#define PM4125_ANALOG_MAX_REGISTER		(PM4125_ANALOG_REGISTERS_MAX_SIZE - 1)
+#define PM4125_DIGITAL_MAX_REGISTER		(PM4125_DIGITAL_REGISTERS_MAX_SIZE - 1)
+#define PM4125_MAX_REGISTER			PM4125_DIGITAL_MAX_REGISTER
+
+#define PM4125_MAX_MICBIAS			3
+#define PM4125_MAX_SWR_CH_IDS			15
+#define PM4125_SWRM_CH_MASK(ch_idx)		BIT(ch_idx - 1)
+
+enum pm4125_tx_sdw_ports {
+	PM4125_ADC_1_2_DMIC1L_BCS_PORT = 1,
+	PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+	PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+};
+
+enum pm4125_rx_sdw_ports {
+	PM4125_HPH_PORT = 1,
+	PM4125_COMP_PORT,
+	PM4125_MAX_SWR_PORTS = PM4125_COMP_PORT,
+};
+
+struct pm4125_sdw_ch_info {
+	int port_num;
+	unsigned int ch_mask;
+	unsigned int master_ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask)		\
+	[id] = {				\
+		.port_num = pn,			\
+		.ch_mask = cmask,		\
+		.master_ch_mask = cmask,	\
+	}
+
+struct pm4125_priv;
+struct pm4125_sdw_priv {
+	struct sdw_slave *sdev;
+	struct sdw_stream_config sconfig;
+	struct sdw_stream_runtime *sruntime;
+	struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+	struct pm4125_sdw_ch_info *ch_info;
+	bool port_enable[PM4125_MAX_SWR_CH_IDS];
+	unsigned int master_channel_map[SDW_MAX_PORTS];
+	int active_ports;
+	int num_ports;
+	bool is_tx;
+	struct pm4125_priv *pm4125;
+	struct irq_domain *slave_irq;
+	struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_PM4125_SDW)
+int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+		    struct snd_soc_dai *dai);
+int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125, struct snd_soc_dai *dai, void *stream,
+			      int direction);
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
+
+struct device *pm4125_sdw_device_get(struct device_node *np);
+
+#else
+static inline int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
+				  struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
+					    struct snd_soc_dai *dai, void *stream, int direction)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125,
+				       struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+enum {
+	/* INTR_CTRL_INT_MASK_0 */
+	PM4125_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+	PM4125_IRQ_MBHC_BUTTON_RELEASE_DET,
+	PM4125_IRQ_MBHC_ELECT_INS_REM_DET,
+	PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	PM4125_IRQ_MBHC_SW_DET,
+	PM4125_IRQ_HPHR_OCP_INT,
+	PM4125_IRQ_HPHR_CNP_INT,
+	PM4125_IRQ_HPHL_OCP_INT,
+
+	/* INTR_CTRL_INT_MASK_1 */
+	PM4125_IRQ_HPHL_CNP_INT,
+	PM4125_IRQ_EAR_CNP_INT,
+	PM4125_IRQ_EAR_SCD_INT,
+	PM4125_IRQ_AUX_CNP_INT,
+	PM4125_IRQ_AUX_SCD_INT,
+	PM4125_IRQ_HPHL_PDM_WD_INT,
+	PM4125_IRQ_HPHR_PDM_WD_INT,
+	PM4125_IRQ_AUX_PDM_WD_INT,
+
+	/* INTR_CTRL_INT_MASK_2 */
+	PM4125_IRQ_LDORT_SCD_INT,
+	PM4125_IRQ_MBHC_MOISTURE_INT,
+	PM4125_IRQ_HPHL_SURGE_DET_INT,
+	PM4125_IRQ_HPHR_SURGE_DET_INT,
+	PM4125_NUM_IRQS,
+};
+
+enum pm4125_tx_sdw_channels {
+	PM4125_ADC1,
+	PM4125_ADC2,
+};
+
+enum pm4125_rx_sdw_channels {
+	PM4125_HPH_L,
+	PM4125_HPH_R,
+	PM4125_COMP_L,
+	PM4125_COMP_R,
+};
+
+#endif /* _PM4125_REGISTERS_H */
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index dcddc28..e3f9b03 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -256,6 +256,161 @@ static const struct reg_sequence rt1320_vc_blind_write[] = {
 	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
 };
 
+static const struct reg_sequence rt1321_blind_write[] = {
+	{ 0x0000c003, 0xf0 },
+	{ 0x0000c01b, 0xfc },
+	{ 0x0000c5c3, 0xf2 },
+	{ 0x0000c5c2, 0x00 },
+	{ 0x0000c5c1, 0x10 },
+	{ 0x0000c5c0, 0x04 },
+	{ 0x0000c5c7, 0x03 },
+	{ 0x0000c5c6, 0x10 },
+	{ 0x0000c526, 0x47 },
+	{ 0x0000c5c4, 0x12 },
+	{ 0x0000c5c5, 0x60 },
+	{ 0x0000c520, 0x10 },
+	{ 0x0000c521, 0x32 },
+	{ 0x0000c5c7, 0x00 },
+	{ 0x0000c5c8, 0x03 },
+	{ 0x0000c5d3, 0x08 },
+	{ 0x0000c5d2, 0x0a },
+	{ 0x0000c5d1, 0x49 },
+	{ 0x0000c5d0, 0x0f },
+	{ 0x0000c580, 0x10 },
+	{ 0x0000c581, 0x32 },
+	{ 0x0000c582, 0x01 },
+	{ 0x0000cb00, 0x03 },
+	{ 0x0000cb02, 0x52 },
+	{ 0x0000cb04, 0x80 },
+	{ 0x0000cb0b, 0x01 },
+	{ 0x0000c682, 0x60 },
+	{ 0x0000c019, 0x10 },
+	{ 0x0000c5f0, 0x01 },
+	{ 0x0000c5f7, 0x22 },
+	{ 0x0000c5f6, 0x22 },
+	{ 0x0000c057, 0x51 },
+	{ 0x0000c054, 0x55 },
+	{ 0x0000c053, 0x55 },
+	{ 0x0000c052, 0x55 },
+	{ 0x0000c051, 0x01 },
+	{ 0x0000c050, 0x15 },
+	{ 0x0000c060, 0x99 },
+	{ 0x0000c030, 0x55 },
+	{ 0x0000c061, 0x55 },
+	{ 0x0000c063, 0x55 },
+	{ 0x0000c065, 0xa5 },
+	{ 0x0000c06b, 0x0a },
+	{ 0x0000ca05, 0xd6 },
+	{ 0x0000ca07, 0x07 },
+	{ 0x0000ca25, 0xd6 },
+	{ 0x0000ca27, 0x07 },
+	{ 0x0000cd00, 0x05 },
+	{ 0x0000c604, 0x40 },
+	{ 0x0000c609, 0x40 },
+	{ 0x0000c046, 0xf7 },
+	{ 0x0000c045, 0xff },
+	{ 0x0000c044, 0xff },
+	{ 0x0000c043, 0xff },
+	{ 0x0000c042, 0xff },
+	{ 0x0000c041, 0xff },
+	{ 0x0000c040, 0xff },
+	{ 0x0000c049, 0xff },
+	{ 0x0000c028, 0x3f },
+	{ 0x0000c020, 0x3f },
+	{ 0x0000c032, 0x13 },
+	{ 0x0000c033, 0x01 },
+	{ 0x0000cc10, 0x01 },
+	{ 0x0000dc20, 0x03 },
+	{ 0x0000de03, 0x05 },
+	{ 0x0000dc00, 0x00 },
+	{ 0x0000c700, 0xf0 },
+	{ 0x0000c701, 0x13 },
+	{ 0x0000c900, 0xc3 },
+	{ 0x0000c570, 0x08 },
+	{ 0x0000c086, 0x02 },
+	{ 0x0000c085, 0x7f },
+	{ 0x0000c084, 0x00 },
+	{ 0x0000c081, 0xff },
+	{ 0x0000f084, 0x0f },
+	{ 0x0000f083, 0xff },
+	{ 0x0000f082, 0xff },
+	{ 0x0000f081, 0xff },
+	{ 0x0000f080, 0xff },
+	{ 0x20003003, 0x3f },
+	{ 0x20005818, 0x81 },
+	{ 0x20009018, 0x81 },
+	{ 0x2000301c, 0x81 },
+	{ 0x0000c003, 0xc0 },
+	{ 0x0000c047, 0x80 },
+	{ 0x0000d541, 0x80 },
+	{ 0x0000d487, 0x0b },
+	{ 0x0000d487, 0x3b },
+	{ 0x0000d486, 0xc3 },
+	{ 0x0000d470, 0x89 },
+	{ 0x0000d471, 0x3a },
+	{ 0x0000d472, 0x1d },
+	{ 0x0000d478, 0xff },
+	{ 0x0000d479, 0x20 },
+	{ 0x0000d47a, 0x10 },
+	{ 0x0000d73c, 0xb7 },
+	{ 0x0000d73d, 0xd7 },
+	{ 0x0000d73e, 0x00 },
+	{ 0x0000d73f, 0x10 },
+	{ 0x3fc2dfc3, 0x00 },
+	{ 0x3fc2dfc2, 0x00 },
+	{ 0x3fc2dfc1, 0x00 },
+	{ 0x3fc2dfc0, 0x07 },
+	{ 0x3fc2dfc7, 0x00 },
+	{ 0x3fc2dfc6, 0x00 },
+	{ 0x3fc2dfc5, 0x00 },
+	{ 0x3fc2dfc4, 0x01 },
+	{ 0x3fc2df83, 0x00 },
+	{ 0x3fc2df82, 0x00 },
+	{ 0x3fc2df81, 0x00 },
+	{ 0x3fc2df80, 0x00 },
+	{ 0x0000d541, 0x40 },
+	{ 0x0000d486, 0x43 },
+	{ 0x1000db00, 0x03 },
+	{ 0x1000db01, 0x00 },
+	{ 0x1000db02, 0x10 },
+	{ 0x1000db03, 0x00 },
+	{ 0x1000db04, 0x00 },
+	{ 0x1000db05, 0x45 },
+	{ 0x1000db06, 0x12 },
+	{ 0x1000db07, 0x09 },
+	{ 0x1000db08, 0x00 },
+	{ 0x1000db09, 0x00 },
+	{ 0x1000db0a, 0x00 },
+	{ 0x1000db0b, 0x13 },
+	{ 0x1000db0c, 0x09 },
+	{ 0x1000db0d, 0x00 },
+	{ 0x1000db0e, 0x00 },
+	{ 0x1000db0f, 0x00 },
+	{ 0x0000d540, 0x21 },
+	{ 0x41000189, 0x00 },
+	{ 0x4100018a, 0x00 },
+	{ 0x41001988, 0x00 },
+	{ 0x41081400, 0x09 },
+	{ 0x40801508, 0x03 },
+	{ 0x40801588, 0x03 },
+	{ 0x40801809, 0x00 },
+	{ 0x4080180a, 0x00 },
+	{ 0x4080180b, 0x00 },
+	{ 0x4080180c, 0x00 },
+	{ 0x40801b09, 0x00 },
+	{ 0x40801b0a, 0x00 },
+	{ 0x40801b0b, 0x00 },
+	{ 0x40801b0c, 0x00 },
+	{ 0x0000d714, 0x17 },
+	{ 0x20009012, 0x00 },
+	{ 0x0000dd0b, 0x0d },
+	{ 0x0000dd0a, 0xff },
+	{ 0x0000dd09, 0x0d },
+	{ 0x0000dd08, 0xff },
+	{ 0x0000d172, 0x2a },
+	{ 0x41001988, 0x03 },
+};
+
 static const struct reg_default rt1320_reg_defaults[] = {
 	{ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
 	{ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
@@ -340,10 +495,19 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg)
 	case 0xf717 ... 0xf719:
 	case 0xf720 ... 0xf723:
 	case 0x1000cd91 ... 0x1000cd96:
+	case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER:
 	case 0x1000f008:
 	case 0x1000f021:
+	case 0x2000300f:
+	case 0x2000301c:
+	case 0x2000900f:
+	case 0x20009018:
+	case 0x3fc29d80 ... 0x3fc29d83:
 	case 0x3fe2e000 ... 0x3fe2e003:
 	case 0x3fc2ab80 ... 0x3fc2abd4:
+	case 0x3fc2bfc0 ... 0x3fc2bfc8:
+	case 0x3fc2d300 ... 0x3fc2d354:
+	case 0x3fc2dfc0 ... 0x3fc2dfc8:
 	/* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
 	case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
 	case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
@@ -394,6 +558,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
 	case 0xc560:
 	case 0xc5b5 ... 0xc5b7:
 	case 0xc5fc ... 0xc5ff:
+	case 0xc680 ... 0xc683:
 	case 0xc820:
 	case 0xc900:
 	case 0xc920:
@@ -412,7 +577,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
 	case 0xd4e5 ... 0xd4e6:
 	case 0xd4e8 ... 0xd4ff:
 	case 0xd530:
-	case 0xd540:
+	case 0xd540 ... 0xd541:
 	case 0xd543:
 	case 0xdb58 ... 0xdb5f:
 	case 0xdb60 ... 0xdb63:
@@ -429,13 +594,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
 	case 0xf01e:
 	case 0xf717 ... 0xf719:
 	case 0xf720 ... 0xf723:
-	case 0x10000000 ... 0x10007fff:
+	case 0x10000000 ... 0x10008fff:
 	case 0x1000c000 ... 0x1000dfff:
 	case 0x1000f008:
 	case 0x1000f021:
+	case 0x2000300f:
+	case 0x2000301c:
+	case 0x2000900f:
+	case 0x20009018:
 	case 0x3fc2ab80 ... 0x3fc2abd4:
+	case 0x3fc2b780:
 	case 0x3fc2bf80 ... 0x3fc2bf83:
-	case 0x3fc2bfc0 ... 0x3fc2bfc7:
+	case 0x3fc2bfc0 ... 0x3fc2bfc8:
+	case 0x3fc2d300 ... 0x3fc2d354:
+	case 0x3fc2dfc0 ... 0x3fc2dfc8:
 	case 0x3fe2e000 ... 0x3fe2e003:
 	case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
 	case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
@@ -560,7 +732,7 @@ static int rt1320_read_prop(struct sdw_slave *slave)
 static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned char func,
 	unsigned char entity, unsigned char ps)
 {
-	unsigned int delay = 1000, val;
+	unsigned int delay = 2000, val;
 
 	pm_runtime_mark_last_busy(&rt1320->sdw_slave->dev);
 
@@ -591,24 +763,44 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
 	struct sdw_slave *slave = rt1320->sdw_slave;
 	const struct firmware *patch;
 	const char *filename;
-	unsigned int addr, val;
+	unsigned int addr, val, min_addr, max_addr;
 	const unsigned char *ptr;
 	int ret, i;
 
-	if (rt1320->version_id <= RT1320_VB)
-		filename = RT1320_VAB_MCU_PATCH;
-	else
-		filename = RT1320_VC_MCU_PATCH;
+	switch (rt1320->dev_id) {
+	case RT1320_DEV_ID:
+		if (rt1320->version_id <= RT1320_VB)
+			filename = RT1320_VAB_MCU_PATCH;
+		else
+			filename = RT1320_VC_MCU_PATCH;
+		min_addr = 0x10007000;
+		max_addr = 0x10007fff;
+		break;
+	case RT1321_DEV_ID:
+		filename = RT1321_VA_MCU_PATCH;
+		min_addr = 0x10008000;
+		max_addr = 0x10008fff;
+		break;
+	default:
+		dev_err(&slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+		return;
+	}
 
 	/* load the patch code here */
 	ret = request_firmware(&patch, filename, &slave->dev);
 	if (ret) {
 		dev_err(&slave->dev, "%s: Failed to load %s firmware", __func__, filename);
 		regmap_write(rt1320->regmap, 0xc598, 0x00);
-		regmap_write(rt1320->regmap, 0x10007000, 0x67);
-		regmap_write(rt1320->regmap, 0x10007001, 0x80);
-		regmap_write(rt1320->regmap, 0x10007002, 0x00);
-		regmap_write(rt1320->regmap, 0x10007003, 0x00);
+		regmap_write(rt1320->regmap, min_addr, 0x67);
+		regmap_write(rt1320->regmap, min_addr + 0x1, 0x80);
+		regmap_write(rt1320->regmap, min_addr + 0x2, 0x00);
+		regmap_write(rt1320->regmap, min_addr + 0x3, 0x00);
+		if (rt1320->dev_id == RT1321_DEV_ID) {
+			regmap_write(rt1320->regmap, 0xd73c, 0x67);
+			regmap_write(rt1320->regmap, 0xd73d, 0x80);
+			regmap_write(rt1320->regmap, 0xd73e, 0x00);
+			regmap_write(rt1320->regmap, 0xd73f, 0x00);
+		}
 	} else {
 		ptr = (const unsigned char *)patch->data;
 		if ((patch->size % 8) == 0) {
@@ -618,7 +810,7 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
 				val = (ptr[i + 4] & 0xff) | (ptr[i + 5] & 0xff) << 8 |
 					(ptr[i + 6] & 0xff) << 16 | (ptr[i + 7] & 0xff) << 24;
 
-				if (addr > 0x10007fff || addr < 0x10007000) {
+				if (addr > max_addr || addr < min_addr) {
 					dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr);
 					goto _exit_;
 				}
@@ -688,6 +880,28 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
 	}
 }
 
+static void rt1321_preset(struct rt1320_sdw_priv *rt1320)
+{
+	unsigned int i, reg, val, delay;
+
+	for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) {
+		reg = rt1321_blind_write[i].reg;
+		val = rt1321_blind_write[i].def;
+		delay = rt1321_blind_write[i].delay_us;
+
+		if (reg == 0x3fc2dfc3)
+			rt1320_load_mcu_patch(rt1320);
+
+		regmap_write(rt1320->regmap, reg, val);
+
+		if (delay)
+			usleep_range(delay, delay + 1000);
+
+		if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+			rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+	}
+}
+
 static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
 {
 	struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
@@ -714,6 +928,9 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
 	if (rt1320->version_id < 0) {
 		regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val);
 		rt1320->version_id = val;
+		regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val);
+		regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp);
+		rt1320->dev_id = (val << 8) | tmp;
 	}
 
 	regmap_read(rt1320->regmap,
@@ -722,16 +939,25 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
 
 	/* initialization write */
 	if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
-		if (rt1320->version_id < RT1320_VC)
-			rt1320_vab_preset(rt1320);
-		else
-			rt1320_vc_preset(rt1320);
+		switch (rt1320->dev_id) {
+		case RT1320_DEV_ID:
+			if (rt1320->version_id < RT1320_VC)
+				rt1320_vab_preset(rt1320);
+			else
+				rt1320_vc_preset(rt1320);
+			break;
+		case RT1321_DEV_ID:
+			rt1321_preset(rt1320);
+			break;
+		default:
+			dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+		}
 
 		regmap_write(rt1320->regmap,
 			SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
 			FUNCTION_NEEDS_INITIALIZATION);
 	}
-	if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA) {
+	if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
 		regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
 			RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0);
 		regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val);
@@ -751,7 +977,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
 		regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
 			RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3);
 	}
-	dev_dbg(dev, "%s version_id=%d\n", __func__, rt1320->version_id);
+	dev_dbg(dev, "%s version_id=%d, dev_id=0x%x\n", __func__, rt1320->version_id, rt1320->dev_id);
 
 	if (rt1320->first_hw_init) {
 		regcache_cache_bypass(rt1320->regmap, false);
@@ -894,12 +1120,20 @@ static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol,
 
 	/* check all channels */
 	for (i = 0; i < p->count; i++) {
-		if (i < 2) {
+		switch (rt1320->dev_id) {
+		case RT1320_DEV_ID:
+			if (i < 2) {
+				reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+				regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+			} else {
+				reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+				regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue[i]);
+			}
+			break;
+		case RT1321_DEV_ID:
 			reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
 			regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
-		} else {
-			reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
-			regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue[i]);
+			break;
 		}
 
 		gain_val[i] = ucontrol->value.integer.value[i];
@@ -916,12 +1150,20 @@ static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol,
 		return 0;
 
 	for (i = 0; i < p->count; i++) {
-		if (i < 2) {
+		switch (rt1320->dev_id) {
+		case RT1320_DEV_ID:
+			if (i < 2) {
+				reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+				err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+			} else {
+				reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+				err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+			}
+			break;
+		case RT1321_DEV_ID:
 			reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
 			err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
-		} else {
-			reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
-			err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+			break;
 		}
 
 		if (err < 0)
@@ -966,12 +1208,20 @@ static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol,
 
 	/* check all channels */
 	for (i = 0; i < p->count; i++) {
-		if (i < 2) {
+		switch (rt1320->dev_id) {
+		case RT1320_DEV_ID:
+			if (i < 2) {
+				reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+				regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+			} else {
+				reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+				regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue);
+			}
+			break;
+		case RT1321_DEV_ID:
 			reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
 			regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
-		} else {
-			reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
-			regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue);
+			break;
 		}
 
 		ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
@@ -989,14 +1239,26 @@ static int rt1320_set_fu_capture_ctl(struct rt1320_sdw_priv *rt1320)
 	for (i = 0; i < ARRAY_SIZE(rt1320->fu_mixer_mute); i++) {
 		ch_mute = (rt1320->fu_dapm_mute || rt1320->fu_mixer_mute[i]) ? 0x01 : 0x00;
 
-		if (i < 2)
+		switch (rt1320->dev_id) {
+		case RT1320_DEV_ID:
+			if (i < 2)
+				err = regmap_write(rt1320->regmap,
+					SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+						RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+			else
+				err = regmap_write(rt1320->regmap,
+					SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
+						RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+			break;
+		case RT1321_DEV_ID:
 			err = regmap_write(rt1320->regmap,
 				SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
 					RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
-		else
-			err = regmap_write(rt1320->regmap,
-				SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
-					RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+			break;
+		default:
+			dev_err(&rt1320->sdw_slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+			return -EINVAL;
+		}
 		if (err < 0)
 			return err;
 	}
@@ -1217,10 +1479,20 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
 		if (dai->id == RT1320_AIF1)
 			port_config.num = 4;
 		else if (dai->id == RT1320_AIF2) {
-			dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
-			dmic_port_config[0].num = 8;
-			dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
-			dmic_port_config[1].num = 10;
+			switch (rt1320->dev_id) {
+			case RT1320_DEV_ID:
+				dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+				dmic_port_config[0].num = 8;
+				dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
+				dmic_port_config[1].num = 10;
+				break;
+			case RT1321_DEV_ID:
+				dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+				dmic_port_config[0].num = 8;
+				break;
+			default:
+				return -EINVAL;
+			}
 		} else
 			return -EINVAL;
 	}
@@ -1228,10 +1500,21 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
 	if (dai->id == RT1320_AIF1)
 		retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
 				&port_config, 1, sdw_stream);
-	else if (dai->id == RT1320_AIF2)
-		retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+	else if (dai->id == RT1320_AIF2) {
+		switch (rt1320->dev_id) {
+		case RT1320_DEV_ID:
+			retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
 				dmic_port_config, 2, sdw_stream);
-	else
+			break;
+		case RT1321_DEV_ID:
+			retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+				dmic_port_config, 1, sdw_stream);
+			break;
+		default:
+			dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+			return -EINVAL;
+		}
+	} else
 		return -EINVAL;
 	if (retval) {
 		dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
@@ -1273,9 +1556,11 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
 		regmap_write(rt1320->regmap,
 			SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
 			sampling_rate);
-		regmap_write(rt1320->regmap,
-			SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
-			sampling_rate);
+
+		if (rt1320->dev_id == RT1320_DEV_ID)
+			regmap_write(rt1320->regmap,
+				SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+				sampling_rate);
 	}
 
 	return 0;
@@ -1469,6 +1754,7 @@ static int rt1320_sdw_remove(struct sdw_slave *slave)
 static const struct sdw_device_id rt1320_id[] = {
 	SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0),
 	SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0),
+	SDW_SLAVE_ENTRY_EXT(0x025d, 0x1321, 0x3, 0x1, 0),
 	{},
 };
 MODULE_DEVICE_TABLE(sdw, rt1320_id);
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
index 23b321a..a6d90e2 100644
--- a/sound/soc/codecs/rt1320-sdw.h
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -14,8 +14,16 @@
 #include <linux/soundwire/sdw_registers.h>
 #include <sound/soc.h>
 
+#define RT1320_DEV_ID 0x6981
+#define RT1321_DEV_ID 0x7045
+
 /* imp-defined registers */
 #define RT1320_DEV_VERSION_ID_1 0xc404
+#define RT1320_DEV_ID_1 0xc405
+#define RT1320_DEV_ID_0 0xc406
+
+#define RT1321_PATCH_MAIN_VER 0x1000cffe
+#define RT1321_PATCH_BETA_VER 0x1000cfff
 
 #define RT1320_KR0_STATUS_CNT 0x1000f008
 #define RT1320_KR0_INT_READY 0x1000f021
@@ -86,6 +94,7 @@ enum rt1320_version_id {
 #define RT1320_VER_B_ID 0x07392238
 #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin"
 #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
+#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
 
 struct rt1320_sdw_priv {
 	struct snd_soc_component *component;
@@ -96,6 +105,7 @@ struct rt1320_sdw_priv {
 	bool hw_init;
 	bool first_hw_init;
 	int version_id;
+	unsigned int dev_id;
 	bool fu_dapm_mute;
 	bool fu_mixer_mute[4];
 };
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
index 582b47d..4d8a12b 100644
--- a/sound/soc/codecs/rt721-sdca-sdw.c
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -63,15 +63,14 @@ static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
 static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
-	case 0x0900007:
+	case 0x0900004 ... 0x0900009:
 	case 0x0a00005:
 	case 0x0c00005:
 	case 0x0d00014:
 	case 0x0310100:
-	case 0x2000001:
-	case 0x2000002:
-	case 0x2000003:
+	case 0x2000000 ... 0x2000003:
 	case 0x2000013:
+	case 0x200002c:
 	case 0x200003c:
 	case 0x2000046:
 	case 0x5810000:
@@ -134,6 +133,8 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
 {
 	switch (reg) {
 	case 0x0310100:
+	case 0x0900005:
+	case 0x0900009:
 	case 0x0a00005:
 	case 0x0c00005:
 	case 0x0d00014:
@@ -141,6 +142,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
 	case 0x200000d:
 	case 0x2000019:
 	case 0x2000020:
+	case 0x200002c:
 	case 0x2000030:
 	case 0x2000046:
 	case 0x2000067:
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index c9c1e60..8baf562 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -180,6 +180,16 @@ static struct tasdevice_config_info *tasdevice_add_config(
 			dev_err(tas_priv->dev, "add conf: Out of boundary\n");
 			goto out;
 		}
+		/* If in the RCA bin file are several profiles with the
+		 * keyword "init", init_profile_id only store the last
+		 * init profile id.
+		 */
+		if (strnstr(&config_data[config_offset], "init", 64)) {
+			tas_priv->rcabin.init_profile_id =
+				tas_priv->rcabin.ncfgs - 1;
+			dev_dbg(tas_priv->dev, "%s: init profile id = %d\n",
+				__func__, tas_priv->rcabin.init_profile_id);
+		}
 		config_offset += 64;
 	}
 
@@ -283,6 +293,8 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw)
 	int i;
 
 	rca = &(tas_priv->rcabin);
+	/* Initialize to none */
+	rca->init_profile_id = -1;
 	fw_hdr = &(rca->fw_hdr);
 	if (!fmw || !fmw->data) {
 		dev_err(tas_priv->dev, "Failed to read %s\n",
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 0e09d79..ea3cdb8 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -1641,6 +1641,12 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
 	tasdevice_prmg_load(tas_priv, 0);
 	tas_priv->cur_prog = 0;
 
+	/* Init common setting for different audio profiles */
+	if (tas_priv->rcabin.init_profile_id >= 0)
+		tasdevice_select_cfg_blk(tas_priv,
+			tas_priv->rcabin.init_profile_id,
+			TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
 #ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
 	if (tas_priv->name_prefix)
 		acoustic_debugfs_node = devm_kasprintf(tas_priv->dev,
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 7399080..715a07a 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -1277,8 +1277,8 @@ static int aic32x4_setup_regulators(struct device *dev,
 	/* Check if the regulator requirements are fulfilled */
 
 	if (IS_ERR(aic32x4->supply_iov)) {
-		dev_err(dev, "Missing supply 'iov'\n");
-		return PTR_ERR(aic32x4->supply_iov);
+		return dev_err_probe(dev, PTR_ERR(aic32x4->supply_iov),
+				     "Missing supply 'iov'\n");
 	}
 
 	if (IS_ERR(aic32x4->supply_ldo)) {
@@ -1286,12 +1286,12 @@ static int aic32x4_setup_regulators(struct device *dev,
 			return -EPROBE_DEFER;
 
 		if (IS_ERR(aic32x4->supply_dv)) {
-			dev_err(dev, "Missing supply 'dv' or 'ldoin'\n");
-			return PTR_ERR(aic32x4->supply_dv);
+			return dev_err_probe(dev, PTR_ERR(aic32x4->supply_dv),
+					     "Missing supply 'dv' or 'ldoin'\n");
 		}
 		if (IS_ERR(aic32x4->supply_av)) {
-			dev_err(dev, "Missing supply 'av' or 'ldoin'\n");
-			return PTR_ERR(aic32x4->supply_av);
+			return dev_err_probe(dev, PTR_ERR(aic32x4->supply_av),
+					     "Missing supply 'av' or 'ldoin'\n");
 		}
 	} else {
 		if (PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
@@ -1383,10 +1383,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap,
 	}
 
 	ret = aic32x4_setup_regulators(dev, aic32x4);
-	if (ret) {
-		dev_err(dev, "Failed to setup regulators\n");
-		return ret;
-	}
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to setup regulators\n");
 
 	if (aic32x4->rstn_gpio) {
 		ndelay(10);
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 423b926..c495be1 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -14,7 +14,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -24,7 +24,6 @@
 #include <sound/initval.h>
 #include <sound/tlv.h>
 
-#include <sound/tlv320dac33-plat.h>
 #include "tlv320dac33.h"
 
 /*
@@ -80,7 +79,7 @@ struct tlv320dac33_priv {
 	struct snd_soc_component *component;
 	struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
 	struct snd_pcm_substream *substream;
-	int power_gpio;
+	struct gpio_desc *reset_gpiod;
 	int chip_power;
 	int irq;
 	unsigned int refclk;
@@ -383,14 +382,26 @@ static int dac33_hard_power(struct snd_soc_component *component, int power)
 			goto exit;
 		}
 
-		if (dac33->power_gpio >= 0)
-			gpio_set_value(dac33->power_gpio, 1);
+		if (dac33->reset_gpiod) {
+			ret = gpiod_set_value(dac33->reset_gpiod, 1);
+			if (ret < 0) {
+				dev_err(&dac33->i2c->dev,
+					"Failed to set reset GPIO: %d\n", ret);
+				goto exit;
+			}
+		}
 
 		dac33->chip_power = 1;
 	} else {
 		dac33_soft_power(component, 0);
-		if (dac33->power_gpio >= 0)
-			gpio_set_value(dac33->power_gpio, 0);
+		if (dac33->reset_gpiod) {
+			ret = gpiod_set_value(dac33->reset_gpiod, 0);
+			if (ret < 0) {
+				dev_err(&dac33->i2c->dev,
+					"Failed to set reset GPIO: %d\n", ret);
+				goto exit;
+			}
+		}
 
 		ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
 					     dac33->supplies);
@@ -1462,16 +1473,9 @@ static struct snd_soc_dai_driver dac33_dai = {
 
 static int dac33_i2c_probe(struct i2c_client *client)
 {
-	struct tlv320dac33_platform_data *pdata;
 	struct tlv320dac33_priv *dac33;
 	int ret, i;
 
-	if (client->dev.platform_data == NULL) {
-		dev_err(&client->dev, "Platform data not set\n");
-		return -ENODEV;
-	}
-	pdata = client->dev.platform_data;
-
 	dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv),
 			     GFP_KERNEL);
 	if (dac33 == NULL)
@@ -1488,26 +1492,22 @@ static int dac33_i2c_probe(struct i2c_client *client)
 
 	i2c_set_clientdata(client, dac33);
 
-	dac33->power_gpio = pdata->power_gpio;
-	dac33->burst_bclkdiv = pdata->burst_bclkdiv;
-	dac33->keep_bclk = pdata->keep_bclk;
-	dac33->mode1_latency = pdata->mode1_latency;
+	if (!dac33->burst_bclkdiv)
+		dac33->burst_bclkdiv = 8;
 	if (!dac33->mode1_latency)
 		dac33->mode1_latency = 10000; /* 10ms */
 	dac33->irq = client->irq;
 	/* Disable FIFO use by default */
 	dac33->fifo_mode = DAC33_FIFO_BYPASS;
 
-	/* Check if the reset GPIO number is valid and request it */
-	if (dac33->power_gpio >= 0) {
-		ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
-		if (ret < 0) {
-			dev_err(&client->dev,
-				"Failed to request reset GPIO (%d)\n",
-				dac33->power_gpio);
-			goto err_gpio;
-		}
-		gpio_direction_output(dac33->power_gpio, 0);
+	/* request optional reset GPIO */
+	dac33->reset_gpiod =
+		devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(dac33->reset_gpiod)) {
+		ret = PTR_ERR(dac33->reset_gpiod);
+		dev_err_probe(&client->dev, ret,
+			      "Failed to get reset GPIO\n");
+		goto err;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
@@ -1518,19 +1518,17 @@ static int dac33_i2c_probe(struct i2c_client *client)
 
 	if (ret != 0) {
 		dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
-		goto err_get;
+		goto err;
 	}
 
 	ret = devm_snd_soc_register_component(&client->dev,
 			&soc_component_dev_tlv320dac33, &dac33_dai, 1);
 	if (ret < 0)
-		goto err_get;
+		goto err;
 
 	return ret;
-err_get:
-	if (dac33->power_gpio >= 0)
-		gpio_free(dac33->power_gpio);
-err_gpio:
+
+err:
 	return ret;
 }
 
@@ -1540,9 +1538,6 @@ static void dac33_i2c_remove(struct i2c_client *client)
 
 	if (unlikely(dac33->chip_power))
 		dac33_hard_power(dac33->component, 0);
-
-	if (dac33->power_gpio >= 0)
-		gpio_free(dac33->power_gpio);
 }
 
 static const struct i2c_device_id tlv320dac33_i2c_id[] = {
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
deleted file mode 100644
index 737ca82..0000000
--- a/sound/soc/codecs/wl1273.c
+++ /dev/null
@@ -1,500 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ALSA SoC WL1273 codec driver
- *
- * Author:      Matti Aaltonen, <matti.j.aaltonen@nokia.com>
- *
- * Copyright:   (C) 2010, 2011 Nokia Corporation
- */
-
-#include <linux/mfd/wl1273-core.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-
-#include "wl1273.h"
-
-enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX };
-
-/* codec private data */
-struct wl1273_priv {
-	enum wl1273_mode mode;
-	struct wl1273_core *core;
-	unsigned int channels;
-};
-
-static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core,
-				      int rate, int width)
-{
-	struct device *dev = &core->client->dev;
-	int r = 0;
-	u16 mode;
-
-	dev_dbg(dev, "rate: %d\n", rate);
-	dev_dbg(dev, "width: %d\n", width);
-
-	mutex_lock(&core->lock);
-
-	mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE;
-
-	switch (rate) {
-	case 48000:
-		mode |= WL1273_IS2_RATE_48K;
-		break;
-	case 44100:
-		mode |= WL1273_IS2_RATE_44_1K;
-		break;
-	case 32000:
-		mode |= WL1273_IS2_RATE_32K;
-		break;
-	case 22050:
-		mode |= WL1273_IS2_RATE_22_05K;
-		break;
-	case 16000:
-		mode |= WL1273_IS2_RATE_16K;
-		break;
-	case 12000:
-		mode |= WL1273_IS2_RATE_12K;
-		break;
-	case 11025:
-		mode |= WL1273_IS2_RATE_11_025;
-		break;
-	case 8000:
-		mode |= WL1273_IS2_RATE_8K;
-		break;
-	default:
-		dev_err(dev, "Sampling rate: %d not supported\n", rate);
-		r = -EINVAL;
-		goto out;
-	}
-
-	switch (width) {
-	case 16:
-		mode |= WL1273_IS2_WIDTH_32;
-		break;
-	case 20:
-		mode |= WL1273_IS2_WIDTH_40;
-		break;
-	case 24:
-		mode |= WL1273_IS2_WIDTH_48;
-		break;
-	case 25:
-		mode |= WL1273_IS2_WIDTH_50;
-		break;
-	case 30:
-		mode |= WL1273_IS2_WIDTH_60;
-		break;
-	case 32:
-		mode |= WL1273_IS2_WIDTH_64;
-		break;
-	case 40:
-		mode |= WL1273_IS2_WIDTH_80;
-		break;
-	case 48:
-		mode |= WL1273_IS2_WIDTH_96;
-		break;
-	case 64:
-		mode |= WL1273_IS2_WIDTH_128;
-		break;
-	default:
-		dev_err(dev, "Data width: %d not supported\n", width);
-		r = -EINVAL;
-		goto out;
-	}
-
-	dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n",  WL1273_I2S_DEF_MODE);
-	dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode);
-	dev_dbg(dev, "mode: 0x%04x\n", mode);
-
-	if (core->i2s_mode != mode) {
-		r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode);
-		if (r)
-			goto out;
-
-		core->i2s_mode = mode;
-		r = core->write(core, WL1273_AUDIO_ENABLE,
-				WL1273_AUDIO_ENABLE_I2S);
-		if (r)
-			goto out;
-	}
-out:
-	mutex_unlock(&core->lock);
-
-	return r;
-}
-
-static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core,
-					    int channel_number)
-{
-	struct device *dev = &core->client->dev;
-	int r = 0;
-
-	dev_dbg(dev, "%s\n", __func__);
-
-	mutex_lock(&core->lock);
-
-	if (core->channel_number == channel_number)
-		goto out;
-
-	if (channel_number == 1 && core->mode == WL1273_MODE_RX)
-		r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO);
-	else if (channel_number == 1 && core->mode == WL1273_MODE_TX)
-		r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO);
-	else if (channel_number == 2 && core->mode == WL1273_MODE_RX)
-		r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO);
-	else if (channel_number == 2 && core->mode == WL1273_MODE_TX)
-		r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO);
-	else
-		r = -EINVAL;
-out:
-	mutex_unlock(&core->lock);
-
-	return r;
-}
-
-static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol,
-				      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
-	ucontrol->value.enumerated.item[0] = wl1273->mode;
-
-	return 0;
-}
-
-/*
- * TODO: Implement the audio routing in the driver. Now this control
- * only indicates the setting that has been done elsewhere (in the user
- * space).
- */
-static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
-
-static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
-				      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
-	if (wl1273->mode == ucontrol->value.enumerated.item[0])
-		return 0;
-
-	/* Do not allow changes while stream is running */
-	if (snd_soc_component_active(component))
-		return -EPERM;
-
-	if (ucontrol->value.enumerated.item[0] >=  ARRAY_SIZE(wl1273_audio_route))
-		return -EINVAL;
-
-	wl1273->mode = ucontrol->value.enumerated.item[0];
-
-	return 1;
-}
-
-static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route);
-
-static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
-				   struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
-	dev_dbg(component->dev, "%s: enter.\n", __func__);
-
-	ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode;
-
-	return 0;
-}
-
-static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
-				   struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-	int val, r = 0;
-
-	dev_dbg(component->dev, "%s: enter.\n", __func__);
-
-	val = ucontrol->value.enumerated.item[0];
-	if (wl1273->core->audio_mode == val)
-		return 0;
-
-	r = wl1273->core->set_audio(wl1273->core, val);
-	if (r < 0)
-		return r;
-
-	return 1;
-}
-
-static const char * const wl1273_audio_strings[] = { "Digital", "Analog" };
-
-static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings);
-
-static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
-				    struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
-	dev_dbg(component->dev, "%s: enter.\n", __func__);
-
-	ucontrol->value.integer.value[0] = wl1273->core->volume;
-
-	return 0;
-}
-
-static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol,
-				    struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-	int r;
-
-	dev_dbg(component->dev, "%s: enter.\n", __func__);
-
-	r = wl1273->core->set_volume(wl1273->core,
-				     ucontrol->value.integer.value[0]);
-	if (r)
-		return r;
-
-	return 1;
-}
-
-static const struct snd_kcontrol_new wl1273_controls[] = {
-	SOC_ENUM_EXT("Codec Mode", wl1273_enum,
-		     snd_wl1273_get_audio_route, snd_wl1273_set_audio_route),
-	SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum,
-		     snd_wl1273_fm_audio_get,  snd_wl1273_fm_audio_put),
-	SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0,
-		       snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
-};
-
-static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
-	SND_SOC_DAPM_INPUT("RX"),
-
-	SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
-	{ "Capture", NULL, "RX" },
-
-	{ "TX", NULL, "Playback" },
-};
-
-static int wl1273_startup(struct snd_pcm_substream *substream,
-			  struct snd_soc_dai *dai)
-{
-	struct snd_soc_component *component = dai->component;
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
-	switch (wl1273->mode) {
-	case WL1273_MODE_BT:
-		snd_pcm_hw_constraint_single(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_RATE, 8000);
-		snd_pcm_hw_constraint_single(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_CHANNELS, 1);
-		break;
-	case WL1273_MODE_FM_RX:
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			pr_err("Cannot play in RX mode.\n");
-			return -EINVAL;
-		}
-		break;
-	case WL1273_MODE_FM_TX:
-		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-			pr_err("Cannot capture in TX mode.\n");
-			return -EINVAL;
-		}
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int wl1273_hw_params(struct snd_pcm_substream *substream,
-			    struct snd_pcm_hw_params *params,
-			    struct snd_soc_dai *dai)
-{
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(dai->component);
-	struct wl1273_core *core = wl1273->core;
-	unsigned int rate, width, r;
-
-	if (params_width(params) != 16) {
-		dev_err(dai->dev, "%d bits/sample not supported\n",
-			params_width(params));
-		return -EINVAL;
-	}
-
-	rate = params_rate(params);
-	width =  hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
-
-	if (wl1273->mode == WL1273_MODE_BT) {
-		if (rate != 8000) {
-			pr_err("Rate %d not supported.\n", params_rate(params));
-			return -EINVAL;
-		}
-
-		if (params_channels(params) != 1) {
-			pr_err("Only mono supported.\n");
-			return -EINVAL;
-		}
-
-		return 0;
-	}
-
-	if (wl1273->mode == WL1273_MODE_FM_TX &&
-	    substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		pr_err("Only playback supported with TX.\n");
-		return -EINVAL;
-	}
-
-	if (wl1273->mode == WL1273_MODE_FM_RX  &&
-	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		pr_err("Only capture supported with RX.\n");
-		return -EINVAL;
-	}
-
-	if (wl1273->mode != WL1273_MODE_FM_RX  &&
-	    wl1273->mode != WL1273_MODE_FM_TX) {
-		pr_err("Unexpected mode: %d.\n", wl1273->mode);
-		return -EINVAL;
-	}
-
-	r = snd_wl1273_fm_set_i2s_mode(core, rate, width);
-	if (r)
-		return r;
-
-	wl1273->channels = params_channels(params);
-	r = snd_wl1273_fm_set_channel_number(core, wl1273->channels);
-	if (r)
-		return r;
-
-	return 0;
-}
-
-static const struct snd_soc_dai_ops wl1273_dai_ops = {
-	.startup	= wl1273_startup,
-	.hw_params	= wl1273_hw_params,
-};
-
-static struct snd_soc_dai_driver wl1273_dai = {
-	.name = "wl1273-fm",
-	.playback = {
-		.stream_name = "Playback",
-		.channels_min = 1,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000_48000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE},
-	.capture = {
-		.stream_name = "Capture",
-		.channels_min = 1,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000_48000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE},
-	.ops = &wl1273_dai_ops,
-};
-
-/* Audio interface format for the soc_card driver */
-int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt)
-{
-	struct wl1273_priv *wl1273;
-
-	if (component == NULL || fmt == NULL)
-		return -EINVAL;
-
-	wl1273 = snd_soc_component_get_drvdata(component);
-
-	switch (wl1273->mode) {
-	case WL1273_MODE_FM_RX:
-	case WL1273_MODE_FM_TX:
-		*fmt =	SND_SOC_DAIFMT_I2S |
-			SND_SOC_DAIFMT_NB_NF |
-			SND_SOC_DAIFMT_CBP_CFP;
-
-		break;
-	case WL1273_MODE_BT:
-		*fmt =	SND_SOC_DAIFMT_DSP_A |
-			SND_SOC_DAIFMT_IB_NF |
-			SND_SOC_DAIFMT_CBP_CFP;
-
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(wl1273_get_format);
-
-static int wl1273_probe(struct snd_soc_component *component)
-{
-	struct wl1273_core **core = component->dev->platform_data;
-	struct wl1273_priv *wl1273;
-
-	dev_dbg(component->dev, "%s.\n", __func__);
-
-	if (!core) {
-		dev_err(component->dev, "Platform data is missing.\n");
-		return -EINVAL;
-	}
-
-	wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL);
-	if (!wl1273)
-		return -ENOMEM;
-
-	wl1273->mode = WL1273_MODE_BT;
-	wl1273->core = *core;
-
-	snd_soc_component_set_drvdata(component, wl1273);
-
-	return 0;
-}
-
-static void wl1273_remove(struct snd_soc_component *component)
-{
-	struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
-
-	dev_dbg(component->dev, "%s\n", __func__);
-	kfree(wl1273);
-}
-
-static const struct snd_soc_component_driver soc_component_dev_wl1273 = {
-	.probe			= wl1273_probe,
-	.remove			= wl1273_remove,
-	.controls		= wl1273_controls,
-	.num_controls		= ARRAY_SIZE(wl1273_controls),
-	.dapm_widgets		= wl1273_dapm_widgets,
-	.num_dapm_widgets	= ARRAY_SIZE(wl1273_dapm_widgets),
-	.dapm_routes		= wl1273_dapm_routes,
-	.num_dapm_routes	= ARRAY_SIZE(wl1273_dapm_routes),
-	.idle_bias_on		= 1,
-	.use_pmdown_time	= 1,
-	.endianness		= 1,
-};
-
-static int wl1273_platform_probe(struct platform_device *pdev)
-{
-	return devm_snd_soc_register_component(&pdev->dev,
-				      &soc_component_dev_wl1273,
-				      &wl1273_dai, 1);
-}
-
-MODULE_ALIAS("platform:wl1273-codec");
-
-static struct platform_driver wl1273_platform_driver = {
-	.driver		= {
-		.name	= "wl1273-codec",
-	},
-	.probe		= wl1273_platform_probe,
-};
-
-module_platform_driver(wl1273_platform_driver);
-
-MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
-MODULE_DESCRIPTION("ASoC WL1273 codec driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h
deleted file mode 100644
index 66c312f..0000000
--- a/sound/soc/codecs/wl1273.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * sound/soc/codec/wl1273.h
- *
- * ALSA SoC WL1273 codec driver
- *
- * Copyright (C) Nokia Corporation
- * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>
- */
-
-#ifndef __WL1273_CODEC_H__
-#define __WL1273_CODEC_H__
-
-int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt);
-
-#endif	/* End of __WL1273_CODEC_H__ */
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 9be4f6c..75d923c 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1536,7 +1536,7 @@ static int wm8993_probe(struct snd_soc_component *component)
 	 * VMID as an output and can disable it.
 	 */
 	if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff)
-		dapm->idle_bias_off = 1;
+		dapm->idle_bias = false;
 
 	return 0;
 
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 240ec1be..128c3a5 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4182,8 +4182,8 @@ static int wm8994_component_probe(struct snd_soc_component *component)
 
 	wm8994->micdet_irq = control->pdata.micdet_irq;
 
-	/* By default use idle_bias_off, will override for WM8994 */
-	dapm->idle_bias_off = 1;
+	/* By default use idle_bias false, will override for WM8994 */
+	dapm->idle_bias = false;
 
 	/* Set revision-specific configuration */
 	switch (control->type) {
@@ -4191,7 +4191,7 @@ static int wm8994_component_probe(struct snd_soc_component *component)
 		/* Single ended line outputs should have VMID on. */
 		if (!control->pdata.lineout1_diff ||
 		    !control->pdata.lineout2_diff)
-			dapm->idle_bias_off = 0;
+			dapm->idle_bias = true;
 
 		switch (control->revision) {
 		case 2:
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index bc584b1..b28398a 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -106,33 +106,33 @@ struct wm8994_priv {
 	int vss_ena[3];
 	int enh_eq_ena[3];
 
-	/* Platform dependant DRC configuration */
+	/* Platform dependent DRC configuration */
 	const char **drc_texts;
 	int drc_cfg[WM8994_NUM_DRC];
 	struct soc_enum drc_enum;
 
-	/* Platform dependant ReTune mobile configuration */
+	/* Platform dependent ReTune mobile configuration */
 	int num_retune_mobile_texts;
 	const char **retune_mobile_texts;
 	int retune_mobile_cfg[WM8994_NUM_EQ];
 	struct soc_enum retune_mobile_enum;
 
-	/* Platform dependant MBC configuration */
+	/* Platform dependent MBC configuration */
 	int mbc_cfg;
 	const char **mbc_texts;
 	struct soc_enum mbc_enum;
 
-	/* Platform dependant VSS configuration */
+	/* Platform dependent VSS configuration */
 	int vss_cfg;
 	const char **vss_texts;
 	struct soc_enum vss_enum;
 
-	/* Platform dependant VSS HPF configuration */
+	/* Platform dependent VSS HPF configuration */
 	int vss_hpf_cfg;
 	const char **vss_hpf_texts;
 	struct soc_enum vss_hpf_enum;
 
-	/* Platform dependant enhanced EQ configuration */
+	/* Platform dependent enhanced EQ configuration */
 	int enh_eq_cfg;
 	const char **enh_eq_texts;
 	struct soc_enum enh_eq_enum;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 459b399..ee20407 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -77,7 +77,7 @@ struct wm8996_priv {
 	int rx_rate[WM8996_AIFS];
 	int bclk_rate[WM8996_AIFS];
 
-	/* Platform dependant ReTune mobile configuration */
+	/* Platform dependent ReTune mobile configuration */
 	int num_retune_mobile_texts;
 	const char **retune_mobile_texts;
 	int retune_mobile_cfg[2];
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 8a1d5cc..82c7633 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1043,7 +1043,7 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		queue_work(system_unbound_wq, &dsp->boot_work);
+		queue_work(system_dfl_wq, &dsp->boot_work);
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
 		wm_adsp_power_down(dsp);
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index 188363b..ca4520a 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -14,6 +14,7 @@
 #include <linux/printk.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 #include <linux/soundwire/sdw.h>
 #include <linux/soundwire/sdw_registers.h>
@@ -468,6 +469,7 @@ struct wsa883x_priv {
 	struct sdw_stream_runtime *sruntime;
 	struct sdw_port_config port_config[WSA883X_MAX_SWR_PORTS];
 	struct gpio_desc *sd_n;
+	struct reset_control *sd_reset;
 	bool port_prepared[WSA883X_MAX_SWR_PORTS];
 	bool port_enable[WSA883X_MAX_SWR_PORTS];
 	int active_ports;
@@ -1546,6 +1548,46 @@ static const struct hwmon_chip_info wsa883x_hwmon_chip_info = {
 	.info	= wsa883x_hwmon_info,
 };
 
+static void wsa883x_reset_assert(void *data)
+{
+	struct wsa883x_priv *wsa883x = data;
+
+	if (wsa883x->sd_reset)
+		reset_control_assert(wsa883x->sd_reset);
+	else
+		gpiod_direction_output(wsa883x->sd_n, 1);
+}
+
+static void wsa883x_reset_deassert(struct wsa883x_priv *wsa883x)
+{
+	if (wsa883x->sd_reset)
+		reset_control_deassert(wsa883x->sd_reset);
+	else
+		gpiod_direction_output(wsa883x->sd_n, 0);
+}
+
+static int wsa883x_get_reset(struct device *dev, struct wsa883x_priv *wsa883x)
+{
+	wsa883x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL);
+	if (IS_ERR(wsa883x->sd_reset))
+		return dev_err_probe(dev, PTR_ERR(wsa883x->sd_reset),
+				     "Failed to get reset\n");
+	/*
+	 * if sd_reset: NULL, so use the backwards compatible way for powerdown-gpios,
+	 * which does not handle sharing GPIO properly.
+	 */
+	if (!wsa883x->sd_reset) {
+		wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
+							GPIOD_FLAGS_BIT_NONEXCLUSIVE |
+							GPIOD_OUT_HIGH);
+		if (IS_ERR(wsa883x->sd_n))
+			return dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
+					     "Shutdown Control GPIO not found\n");
+	}
+
+	return 0;
+}
+
 static int wsa883x_probe(struct sdw_slave *pdev,
 			 const struct sdw_device_id *id)
 {
@@ -1566,13 +1608,9 @@ static int wsa883x_probe(struct sdw_slave *pdev,
 	if (ret)
 		return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n");
 
-	wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
-						GPIOD_FLAGS_BIT_NONEXCLUSIVE | GPIOD_OUT_HIGH);
-	if (IS_ERR(wsa883x->sd_n)) {
-		ret = dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
-				    "Shutdown Control GPIO not found\n");
+	ret = wsa883x_get_reset(dev, wsa883x);
+	if (ret)
 		goto err;
-	}
 
 	dev_set_drvdata(dev, wsa883x);
 	wsa883x->slave = pdev;
@@ -1595,11 +1633,14 @@ static int wsa883x_probe(struct sdw_slave *pdev,
 	pdev->prop.simple_clk_stop_capable = true;
 	pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
 	pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
-	gpiod_direction_output(wsa883x->sd_n, 0);
+
+	wsa883x_reset_deassert(wsa883x);
+	ret = devm_add_action_or_reset(dev, wsa883x_reset_assert, wsa883x);
+	if (ret)
+		return ret;
 
 	wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config);
 	if (IS_ERR(wsa883x->regmap)) {
-		gpiod_direction_output(wsa883x->sd_n, 1);
 		ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap),
 				    "regmap_init failed\n");
 		goto err;
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index d0367b2..757e7868 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1244,7 +1244,6 @@ static struct regmap_config fsl_sai_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
-	.fast_io = true,
 
 	.max_register = FSL_SAI_RMR,
 	.reg_defaults = fsl_sai_reg_defaults_ofs0,
@@ -1346,7 +1345,7 @@ static int fsl_sai_read_dlcfg(struct fsl_sai *sai)
 
 	num_cfg = elems / 3;
 	/*  Add one more for default value */
-	cfg = devm_kzalloc(&pdev->dev, (num_cfg + 1) * sizeof(*cfg), GFP_KERNEL);
+	cfg = devm_kcalloc(&pdev->dev, num_cfg + 1, sizeof(*cfg), GFP_KERNEL);
 	if (!cfg)
 		return -ENOMEM;
 
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index cc2918e..f8335a0 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -305,7 +305,7 @@ static int imx_audmux_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	regcache = devm_kzalloc(&pdev->dev, sizeof(u32) * reg_max, GFP_KERNEL);
+	regcache = devm_kcalloc(&pdev->dev, reg_max, sizeof(u32), GFP_KERNEL);
 	if (!regcache)
 		return -ENOMEM;
 
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index fe47b43..1115189 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -101,7 +101,6 @@ static int imx_hdmi_probe(struct platform_device *pdev)
 	bool hdmi_out = of_property_read_bool(np, "hdmi-out");
 	bool hdmi_in = of_property_read_bool(np, "hdmi-in");
 	struct snd_soc_dai_link_component *dlc;
-	struct platform_device *cpu_pdev;
 	struct device_node *cpu_np;
 	struct imx_hdmi_data *data;
 	int ret;
@@ -117,17 +116,9 @@ static int imx_hdmi_probe(struct platform_device *pdev)
 		goto fail;
 	}
 
-	cpu_pdev = of_find_device_by_node(cpu_np);
-	if (!cpu_pdev) {
-		dev_err(&pdev->dev, "failed to find SAI platform device\n");
-		ret = -EINVAL;
-		goto fail;
-	}
-
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data) {
 		ret = -ENOMEM;
-		put_device(&cpu_pdev->dev);
 		goto fail;
 	}
 
@@ -140,15 +131,13 @@ static int imx_hdmi_probe(struct platform_device *pdev)
 
 	data->dai.name = "i.MX HDMI";
 	data->dai.stream_name = "i.MX HDMI";
-	data->dai.cpus->dai_name = dev_name(&cpu_pdev->dev);
+	data->dai.cpus->of_node = cpu_np;
 	data->dai.platforms->of_node = cpu_np;
 	data->dai.ops = &imx_hdmi_ops;
 	data->dai.playback_only = true;
 	data->dai.capture_only = false;
 	data->dai.init = imx_hdmi_init;
 
-	put_device(&cpu_pdev->dev);
-
 	if (of_node_name_eq(cpu_np, "sai")) {
 		data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
 		data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c
index 89b9959..2e49066 100644
--- a/sound/soc/generic/test-component.c
+++ b/sound/soc/generic/test-component.c
@@ -547,8 +547,8 @@ static int test_driver_probe(struct platform_device *pdev)
 
 	priv	= devm_kzalloc(dev, sizeof(*priv),		GFP_KERNEL);
 	cdriv	= devm_kzalloc(dev, sizeof(*cdriv),		GFP_KERNEL);
-	ddriv	= devm_kzalloc(dev, sizeof(*ddriv) * num,	GFP_KERNEL);
-	dname	= devm_kzalloc(dev, sizeof(*dname) * num,	GFP_KERNEL);
+	ddriv	= devm_kcalloc(dev, num, sizeof(*ddriv), 	GFP_KERNEL);
+	dname	= devm_kcalloc(dev, num, sizeof(*dname), 	GFP_KERNEL);
 	if (!priv || !cdriv || !ddriv || !dname || !adata)
 		return -EINVAL;
 
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index e0357d2..3c47c8de 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -64,7 +64,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
 		header.p.header_high.part.done = 0;
 		sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
 
-		/* write 1 to clear status register */;
+		/* write 1 to clear status register */
 		isr.part.done_interrupt = 1;
 		sst_shim_write64(drv->shim, SST_ISRX, isr.full);
 		spin_unlock(&drv->ipc_spin_lock);
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
index 3dccf0a..b922eea 100644
--- a/sound/soc/intel/avs/apl.c
+++ b/sound/soc/intel/avs/apl.c
@@ -10,6 +10,7 @@
 #include <linux/slab.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 #include "path.h"
 #include "registers.h"
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 4c096af..0f8ddd0 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -22,7 +22,6 @@
 struct avs_dev;
 struct avs_tplg;
 struct avs_tplg_library;
-struct avs_soc_component;
 struct avs_ipc_msg;
 
 #ifdef CONFIG_ACPI
@@ -348,91 +347,18 @@ struct avs_soc_component {
 
 extern const struct snd_soc_dai_ops avs_dai_fe_ops;
 
-int avs_soc_component_register(struct device *dev, const char *name,
-			       struct snd_soc_component_driver *drv,
-			       struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
-int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
-int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
-			      unsigned long *tdms);
-int avs_hda_platform_register(struct avs_dev *adev, const char *name);
+int avs_register_dmic_component(struct avs_dev *adev, const char *name);
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+			       unsigned long *tdms);
+int avs_register_hda_component(struct avs_dev *adev, const char *name);
+int avs_register_component(struct device *dev, const char *name,
+			   struct snd_soc_component_driver *drv,
+			   struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
 
 int avs_register_all_boards(struct avs_dev *adev);
 void avs_unregister_all_boards(struct avs_dev *adev);
 
-/* Firmware tracing helpers */
-
-#define avs_log_buffer_size(adev) \
-	((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
-
-#define avs_log_buffer_addr(adev, core) \
-({ \
-	s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
-	(__offset < 0) ? NULL : \
-			 (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
-})
-
-static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
-{
-	unsigned long flags;
-	int ret;
-
-	spin_lock_irqsave(&adev->trace_lock, flags);
-	ret = avs_dsp_op(adev, log_buffer_status, msg);
-	spin_unlock_irqrestore(&adev->trace_lock, flags);
-
-	return ret;
-}
-
-struct avs_apl_log_buffer_layout {
-	u32 read_ptr;
-	u32 write_ptr;
-	u8 buffer[];
-} __packed;
-static_assert(sizeof(struct avs_apl_log_buffer_layout) == 8);
-
-#define avs_apl_log_payload_size(adev) \
-	(avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout))
-
-#define avs_apl_log_payload_addr(addr) \
-	(addr + sizeof(struct avs_apl_log_buffer_layout))
-
-#ifdef CONFIG_DEBUG_FS
-#define AVS_SET_ENABLE_LOGS_OP(name) \
-	.enable_logs = avs_##name##_enable_logs
-
-bool avs_logging_fw(struct avs_dev *adev);
-void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
-void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
-
-int avs_probe_platform_register(struct avs_dev *adev, const char *name);
-
-void avs_debugfs_init(struct avs_dev *adev);
-void avs_debugfs_exit(struct avs_dev *adev);
-#else
-#define AVS_SET_ENABLE_LOGS_OP(name)
-
-static inline bool avs_logging_fw(struct avs_dev *adev)
-{
-	return false;
-}
-
-static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
-{
-}
-
-static inline void
-avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len)
-{
-}
-
-static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name)
-{
-	return 0;
-}
-
-static inline void avs_debugfs_init(struct avs_dev *adev) { }
-static inline void avs_debugfs_exit(struct avs_dev *adev) { }
-#endif
+int avs_parse_sched_cfg(struct avs_dev *adev, const char *buf, size_t len);
 
 /* Filesystems integration */
 
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index fb49167..52e6266 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -17,6 +17,8 @@
 #include <sound/soc-acpi.h>
 #include <sound/soc-component.h>
 #include "avs.h"
+#include "debug.h"
+#include "pcm.h"
 #include "utils.h"
 
 static char *i2s_test;
@@ -56,19 +58,13 @@ static const struct dmi_system_id kblr_dmi_table[] = {
 static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
 {
 	struct snd_soc_acpi_mach *mach = arg;
-	const struct dmi_system_id *dmi_id;
 	struct dmi_system_id *dmi_table;
 
-	if (mach->quirk_data == NULL)
-		return mach;
-
 	dmi_table = (struct dmi_system_id *)mach->quirk_data;
 
-	dmi_id = dmi_first_match(dmi_table);
-	if (!dmi_id)
-		return NULL;
-
-	return mach;
+	if (!dmi_table || dmi_first_match(dmi_table))
+		return mach;
+	return NULL;
 }
 
 #define AVS_SSP(x)		(BIT(x))
@@ -368,10 +364,10 @@ struct avs_acpi_boards {
 
 /* supported I2S boards per platform */
 static const struct avs_acpi_boards i2s_boards[] = {
-	AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines),
-	AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines),
-	AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines),
-	AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines),
+	AVS_MACH_ENTRY(HDA_SKL_LP,	avs_skl_i2s_machines),
+	AVS_MACH_ENTRY(HDA_KBL_LP,	avs_kbl_i2s_machines),
+	AVS_MACH_ENTRY(HDA_APL,		avs_apl_i2s_machines),
+	AVS_MACH_ENTRY(HDA_GML,		avs_gml_i2s_machines),
 	AVS_MACH_ENTRY(HDA_CNL_LP,	avs_cnl_i2s_machines),
 	AVS_MACH_ENTRY(HDA_CNL_H,	avs_cnl_i2s_machines),
 	AVS_MACH_ENTRY(HDA_CML_LP,	avs_cnl_i2s_machines),
@@ -386,185 +382,122 @@ static const struct avs_acpi_boards i2s_boards[] = {
 	{ },
 };
 
-static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+static struct snd_soc_acpi_mach *avs_get_i2s_machines(struct avs_dev *adev)
 {
 	int id, i;
 
 	id = adev->base.pci->device;
 	for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
 		if (i2s_boards[i].id == id)
-			return &i2s_boards[i];
+			return i2s_boards[i].machs;
 	return NULL;
 }
 
-/* platform devices owned by AVS audio are removed with this hook */
-static void board_pdev_unregister(void *data)
+/* Platform devices spawned by AVS driver are removed with this hook. */
+static void avs_unregister_board(void *pdev)
 {
-	platform_device_unregister(data);
+	platform_device_unregister(pdev);
+}
+
+static struct platform_device *avs_register_board(struct avs_dev *adev, const char *name,
+						  const void *data, size_t size)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	pdev = platform_device_register_data(NULL, name, PLATFORM_DEVID_AUTO, data, size);
+	if (IS_ERR(pdev))
+		return pdev;
+
+	ret = devm_add_action_or_reset(adev->dev, avs_unregister_board, pdev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return pdev;
+}
+
+static struct platform_device *avs_register_board_pdata(struct avs_dev *adev, const char *name,
+							struct snd_soc_acpi_mach *mach,
+							struct hda_codec *codec,
+							unsigned long *tdms, char *codec_name)
+{
+	struct avs_mach_pdata *pdata;
+
+	pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->codec = codec;
+	pdata->tdms = tdms;
+	pdata->codec_name = codec_name;
+	pdata->obsolete_card_names = obsolete_card_names;
+	mach->pdata = pdata;
+
+	return avs_register_board(adev, name, mach, sizeof(*mach));
 }
 
 static int __maybe_unused avs_register_probe_board(struct avs_dev *adev)
 {
-	struct platform_device *board;
-	struct snd_soc_acpi_mach mach = {{0}};
-	int ret;
+	struct platform_device *pdev;
 
-	ret = avs_probe_platform_register(adev, "probe-platform");
-	if (ret < 0)
-		return ret;
+	pdev = avs_register_board(adev, "avs_probe_mb", NULL, 0);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
 
-	mach.mach_params.platform = "probe-platform";
-
-	board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE,
-					      (const void *)&mach, sizeof(mach));
-	if (IS_ERR(board)) {
-		dev_err(adev->dev, "probe board register failed\n");
-		return PTR_ERR(board);
-	}
-
-	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
-	if (ret < 0) {
-		platform_device_unregister(board);
-		return ret;
-	}
-	return 0;
+	return avs_register_probe_component(adev, dev_name(&pdev->dev));
 }
 
 static int avs_register_dmic_board(struct avs_dev *adev)
 {
-	struct platform_device *codec, *board;
-	struct snd_soc_acpi_mach mach = {{0}};
-	struct avs_mach_pdata *pdata;
-	int ret;
+	static struct snd_soc_acpi_mach mach = {
+		.tplg_filename = "dmic-tplg.bin",
+	};
+	struct platform_device *pdev;
+	char *codec_name;
 
 	if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) {
 		dev_dbg(adev->dev, "no DMIC endpoints present\n");
 		return 0;
 	}
 
-	codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
-	if (IS_ERR(codec)) {
-		dev_err(adev->dev, "dmic codec register failed\n");
-		return PTR_ERR(codec);
-	}
+	/* DMIC present in Intel PCH is enumerated statically. */
+	pdev = avs_register_board(adev, "dmic-codec", NULL, 0);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
 
-	ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
-	if (ret < 0) {
-		platform_device_unregister(codec);
-		return ret;
-	}
-
-	ret = avs_dmic_platform_register(adev, "dmic-platform");
-	if (ret < 0)
-		return ret;
-
-	pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		return -ENOMEM;
-	pdata->obsolete_card_names = obsolete_card_names;
-	mach.pdata = pdata;
-	mach.tplg_filename = "dmic-tplg.bin";
-	mach.mach_params.platform = "dmic-platform";
-
-	board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
-					(const void *)&mach, sizeof(mach));
-	if (IS_ERR(board)) {
-		dev_err(adev->dev, "dmic board register failed\n");
-		return PTR_ERR(board);
-	}
-
-	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
-	if (ret < 0) {
-		platform_device_unregister(board);
-		return ret;
-	}
-
-	return 0;
-}
-
-static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
-{
-	struct platform_device *board;
-	struct avs_mach_pdata *pdata;
-	int num_ssps;
-	char *name;
-	int ret;
-	int uid;
-
-	num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
-	if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
-		dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
-			num_ssps, mach->drv_name,
-			(unsigned long)__fls(mach->mach_params.i2s_link_mask));
-		return -ENODEV;
-	}
-
-	pdata = mach->pdata;
-	if (!pdata)
-		pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		return -ENOMEM;
-	pdata->obsolete_card_names = obsolete_card_names;
-	mach->pdata = pdata;
-
-	uid = mach->mach_params.i2s_link_mask;
-	if (avs_mach_singular_ssp(mach))
-		uid = (uid << AVS_CHANNELS_MAX) + avs_mach_ssp_tdm(mach, avs_mach_ssp_port(mach));
-
-	name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, uid);
-	if (!name)
+	codec_name = devm_kstrdup(adev->dev, dev_name(&pdev->dev), GFP_KERNEL);
+	if (!codec_name)
 		return -ENOMEM;
 
-	ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, pdata->tdms);
-	if (ret < 0)
-		return ret;
+	pdev = avs_register_board_pdata(adev, "avs_dmic", &mach, NULL, NULL, codec_name);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
 
-	mach->mach_params.platform = name;
-
-	board = platform_device_register_data(NULL, mach->drv_name, uid,
-					      (const void *)mach, sizeof(*mach));
-	if (IS_ERR(board)) {
-		dev_err(adev->dev, "ssp board register failed\n");
-		return PTR_ERR(board);
-	}
-
-	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
-	if (ret < 0) {
-		platform_device_unregister(board);
-		return ret;
-	}
-
-	return 0;
+	return avs_register_dmic_component(adev, dev_name(&pdev->dev));
 }
 
 static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot)
 {
-	struct snd_soc_acpi_mach *mach;
-	int tdm_mask = BIT(tdm_slot);
-	unsigned long *tdm_cfg;
-	char *tplg_name;
-	int ret;
+	struct snd_soc_acpi_mach mach = {{0}};
+	struct platform_device *pdev;
+	unsigned long *tdms;
 
-	mach = devm_kzalloc(adev->dev, sizeof(*mach), GFP_KERNEL);
-	tdm_cfg = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(unsigned long), GFP_KERNEL);
-	tplg_name = devm_kasprintf(adev->dev, GFP_KERNEL, AVS_STRING_FMT("i2s", "-test-tplg.bin",
-				   ssp_port, tdm_slot));
-	if (!mach || !tdm_cfg || !tplg_name)
+	tdms = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(*tdms), GFP_KERNEL);
+	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
+					    AVS_STRING_FMT("i2s", "-test-tplg.bin",
+							   ssp_port, tdm_slot));
+	if (!tdms || !mach.tplg_filename)
 		return -ENOMEM;
 
-	mach->drv_name = "avs_i2s_test";
-	mach->mach_params.i2s_link_mask = AVS_SSP(ssp_port);
-	tdm_cfg[ssp_port] = tdm_mask;
-	mach->pdata = tdm_cfg;
-	mach->tplg_filename = tplg_name;
+	tdms[ssp_port] = BIT(tdm_slot);
+	mach.drv_name = "avs_i2s_test";
+	mach.mach_params.i2s_link_mask = AVS_SSP(ssp_port);
 
-	ret = avs_register_i2s_board(adev, mach);
-	if (ret < 0) {
-		dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
-		return ret;
-	}
+	pdev = avs_register_board_pdata(adev, mach.drv_name, &mach, NULL, tdms, NULL);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
 
-	return 0;
+	return avs_register_i2s_component(adev, dev_name(&pdev->dev), AVS_SSP(ssp_port), tdms);
 }
 
 static int avs_register_i2s_test_boards(struct avs_dev *adev)
@@ -574,6 +507,9 @@ static int avs_register_i2s_test_boards(struct avs_dev *adev)
 	unsigned long tdm_slots;
 	u32 *array, num_elems;
 
+	if (!i2s_test)
+		return 0;
+
 	ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array);
 	if (ret) {
 		dev_err(adev->dev, "failed to parse i2s_test parameter\n");
@@ -599,9 +535,26 @@ static int avs_register_i2s_test_boards(struct avs_dev *adev)
 	return 0;
 }
 
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+	u32 i2s_mask = mach->mach_params.i2s_link_mask;
+	struct platform_device *pdev;
+	unsigned long *tdms = NULL;
+
+	if (mach->pdata)
+		tdms = ((struct avs_mach_pdata *)mach->pdata)->tdms;
+
+	pdev = avs_register_board_pdata(adev, mach->drv_name, mach, NULL, tdms, NULL);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
+
+	return avs_register_i2s_component(adev, dev_name(&pdev->dev), i2s_mask, tdms);
+}
+
 static int avs_register_i2s_boards(struct avs_dev *adev)
 {
-	const struct avs_acpi_boards *boards;
+	int num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+	struct snd_soc_acpi_mach *machs;
 	struct snd_soc_acpi_mach *mach;
 	int ret;
 
@@ -610,19 +563,22 @@ static int avs_register_i2s_boards(struct avs_dev *adev)
 		return 0;
 	}
 
-	if (i2s_test)
-		return avs_register_i2s_test_boards(adev);
-
-	boards = avs_get_i2s_boards(adev);
-	if (!boards) {
+	machs = avs_get_i2s_machines(adev);
+	if (!machs) {
 		dev_dbg(adev->dev, "no I2S endpoints supported\n");
 		return 0;
 	}
 
-	for (mach = boards->machs; mach->id[0]; mach++) {
+	for (mach = machs; mach->id[0]; mach++) {
 		if (!acpi_dev_present(mach->id, mach->uid, -1))
 			continue;
 
+		if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+			dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+				num_ssps, mach->drv_name,
+				(unsigned long)__fls(mach->mach_params.i2s_link_mask));
+			continue;
+		}
 		if (mach->machine_quirk)
 			if (!mach->machine_quirk(mach))
 				continue;
@@ -637,49 +593,20 @@ static int avs_register_i2s_boards(struct avs_dev *adev)
 
 static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
 {
-	struct snd_soc_acpi_mach mach = {{0}};
-	struct platform_device *board;
-	struct avs_mach_pdata *pdata;
 	struct hdac_device *hdev = &codec->core;
-	char *pname;
-	int ret, id;
+	struct snd_soc_acpi_mach mach = {{0}};
+	struct platform_device *pdev;
 
-	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
-	if (!pname)
-		return -ENOMEM;
-
-	pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
-		return -ENOMEM;
-	pdata->obsolete_card_names = obsolete_card_names;
-	pdata->codec = codec;
-
-	ret = avs_hda_platform_register(adev, pname);
-	if (ret < 0)
-		return ret;
-
-	mach.pdata = pdata;
-	mach.mach_params.platform = pname;
 	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
 					    hdev->vendor_id);
 	if (!mach.tplg_filename)
 		return -ENOMEM;
 
-	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
-	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
-					      sizeof(mach));
-	if (IS_ERR(board)) {
-		dev_err(adev->dev, "hda board register failed\n");
-		return PTR_ERR(board);
-	}
+	pdev = avs_register_board_pdata(adev, "avs_hdaudio", &mach, codec, NULL, NULL);
+	if (IS_ERR(pdev))
+		return PTR_ERR(pdev);
 
-	ret = devm_add_action(adev->dev, board_pdev_unregister, board);
-	if (ret < 0) {
-		platform_device_unregister(board);
-		return ret;
-	}
-
-	return 0;
+	return avs_register_hda_component(adev, dev_name(&pdev->dev));
 }
 
 static int avs_register_hda_boards(struct avs_dev *adev)
@@ -722,6 +649,10 @@ int avs_register_all_boards(struct avs_dev *adev)
 		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
 			 ret);
 
+	ret = avs_register_i2s_test_boards(adev);
+	if (ret)
+		dev_dbg(adev->dev, "enumerate I2S TEST endpoints failed: %d\n", ret);
+
 	ret = avs_register_i2s_boards(adev);
 	if (ret < 0)
 		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
index 3ef0db2..6782dc7 100644
--- a/sound/soc/intel/avs/boards/da7219.c
+++ b/sound/soc/intel/avs/boards/da7219.c
@@ -165,8 +165,8 @@ avs_da7219_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_param
 	return 0;
 }
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -176,8 +176,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
@@ -193,6 +191,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -218,18 +217,16 @@ static int avs_da7219_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct snd_soc_jack *jack;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -259,10 +256,6 @@ static int avs_da7219_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, jack);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
index a1448a9..bf6f580 100644
--- a/sound/soc/intel/avs/boards/dmic.c
+++ b/sound/soc/intel/avs/boards/dmic.c
@@ -14,30 +14,6 @@
 
 SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC Pin")));
 SND_SOC_DAILINK_DEF(dmic_wov_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC WoV Pin")));
-SND_SOC_DAILINK_DEF(dmic_codec, DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
-/* Name overridden on probe */
-SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("")));
-
-static struct snd_soc_dai_link card_dai_links[] = {
-	/* Back ends */
-	{
-		.name = "DMIC",
-		.id = 0,
-		.capture_only = 1,
-		.nonatomic = 1,
-		.no_pcm = 1,
-		SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform),
-	},
-	{
-		.name = "DMIC WoV",
-		.id = 1,
-		.capture_only = 1,
-		.nonatomic = 1,
-		.no_pcm = 1,
-		.ignore_suspend = 1,
-		SND_SOC_DAILINK_REG(dmic_wov_pin, dmic_codec, platform),
-	},
-};
 
 static const struct snd_soc_dapm_widget card_widgets[] = {
 	SND_SOC_DAPM_MIC("SoC DMIC", NULL),
@@ -47,12 +23,56 @@ static const struct snd_soc_dapm_route card_routes[] = {
 	{"DMic", NULL, "SoC DMIC"},
 };
 
+static int avs_create_dai_links(struct device *dev, const char *codec_name,
+				struct snd_soc_dai_link **links, int *num_links)
+{
+	struct snd_soc_dai_link_component *platform;
+	struct snd_soc_dai_link *dl;
+	const int num_dl = 2;
+
+	dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
+	platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+	if (!dl || !platform)
+		return -ENOMEM;
+
+	dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+	if (!dl->codecs)
+		return -ENOMEM;
+
+	dl->codecs->name = devm_kstrdup(dev, codec_name, GFP_KERNEL);
+	dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, "dmic-hifi");
+	if (!dl->codecs->name || !dl->codecs->dai_name)
+		return -ENOMEM;
+
+	platform->name = dev_name(dev);
+	dl[0].num_cpus = 1;
+	dl[0].num_codecs = 1;
+	dl[0].platforms = platform;
+	dl[0].num_platforms = 1;
+	dl[0].nonatomic = 1;
+	dl[0].no_pcm = 1;
+	dl[0].capture_only = 1;
+	memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+	dl[0].name = "DMIC";
+	dl[0].cpus = dmic_pin;
+	dl[0].id = 0;
+	dl[1].name = "DMIC WoV";
+	dl[1].cpus = dmic_wov_pin;
+	dl[1].id = 1;
+	dl[1].ignore_suspend = 1;
+
+	*links = dl;
+	*num_links = num_dl;
+	return 0;
+}
+
 static int avs_dmic_probe(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
 	struct snd_soc_acpi_mach *mach;
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
-	struct device *dev = &pdev->dev;
 	int ret;
 
 	mach = dev_get_platdata(dev);
@@ -62,6 +82,10 @@ static int avs_dmic_probe(struct platform_device *pdev)
 	if (!card)
 		return -ENOMEM;
 
+	ret = avs_create_dai_links(dev, pdata->codec_name, &card->dai_link, &card->num_links);
+	if (ret)
+		return ret;
+
 	if (pdata->obsolete_card_names) {
 		card->name = "avs_dmic";
 	} else {
@@ -70,18 +94,12 @@ static int avs_dmic_probe(struct platform_device *pdev)
 	}
 	card->dev = dev;
 	card->owner = THIS_MODULE;
-	card->dai_link = card_dai_links;
-	card->num_links = ARRAY_SIZE(card_dai_links);
 	card->dapm_widgets = card_widgets;
 	card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
 	card->dapm_routes = card_routes;
 	card->num_dapm_routes = ARRAY_SIZE(card_routes);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
index 1955f2d..eb2b408 100644
--- a/sound/soc/intel/avs/boards/es8336.c
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -132,7 +132,7 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
 	snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
 	snd_soc_component_set_jack(component, &data->jack, NULL);
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	return 0;
 }
@@ -195,8 +195,9 @@ static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime,
 
 	return 0;
 }
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -206,8 +207,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -222,6 +221,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -263,18 +263,16 @@ static int avs_es8336_probe(struct platform_device *pdev)
 	struct avs_card_drvdata *data;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -306,10 +304,6 @@ static int avs_es8336_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, data);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index 19b2255..aec769e 100644
--- a/sound/soc/intel/avs/boards/hdaudio.c
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -16,7 +16,7 @@
 #include "../utils.h"
 
 static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
-				const char *platform_name, struct snd_soc_dai_link **links)
+				struct snd_soc_dai_link **links)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -29,7 +29,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
+	platform->name = dev_name(dev);
 	pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
 
 	for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
@@ -142,7 +142,7 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
 	list_for_each_entry(pcm, &codec->pcm_list_head, list)
 		pcm_count++;
 
-	ret = avs_create_dai_links(card->dev, codec, pcm_count, mach->mach_params.platform, &links);
+	ret = avs_create_dai_links(card->dev, codec, pcm_count, &links);
 	if (ret < 0) {
 		dev_err(card->dev, "create links failed: %d\n", ret);
 		return ret;
@@ -197,7 +197,7 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
 	if (!binder->codecs->name)
 		return -ENOMEM;
 
-	binder->platforms->name = mach->mach_params.platform;
+	binder->platforms->name = dev_name(dev);
 	binder->num_platforms = 1;
 	binder->codecs->dai_name = "codec-probing-DAI";
 	binder->num_codecs = 1;
@@ -207,7 +207,10 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	if (pdata->obsolete_card_names) {
-		card->name = binder->codecs->name;
+		card->name = devm_kasprintf(dev, GFP_KERNEL, "hdaudioB%dD%d", codec->bus->core.idx,
+					    codec->core.addr);
+		if (!card->name)
+			return -ENOMEM;
 	} else {
 		card->driver_name = "avs_hdaudio";
 		if (hda_codec_is_display(codec))
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
index f7b6d77..9a6b89f 100644
--- a/sound/soc/intel/avs/boards/i2s_test.c
+++ b/sound/soc/intel/avs/boards/i2s_test.c
@@ -14,8 +14,8 @@
 #include <sound/soc-dapm.h>
 #include "../utils.h"
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -25,8 +25,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -39,6 +37,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -59,11 +58,9 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	if (!avs_mach_singular_ssp(mach)) {
@@ -94,7 +91,7 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
 	if (!card->name)
 		return -ENOMEM;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d\n", ret);
 		return ret;
@@ -106,10 +103,6 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
 	card->num_links = 1;
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
index 72053f8..e9a8780 100644
--- a/sound/soc/intel/avs/boards/max98357a.c
+++ b/sound/soc/intel/avs/boards/max98357a.c
@@ -46,8 +46,8 @@ avs_max98357a_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_pa
 	return 0;
 }
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -57,8 +57,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -73,6 +71,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -96,18 +95,16 @@ static int avs_max98357a_probe(struct platform_device *pdev)
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -135,10 +132,6 @@ static int avs_max98357a_probe(struct platform_device *pdev)
 	card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
index cdba1c3..8b45b64 100644
--- a/sound/soc/intel/avs/boards/max98373.c
+++ b/sound/soc/intel/avs/boards/max98373.c
@@ -95,8 +95,8 @@ static const struct snd_soc_ops avs_max98373_ops = {
 	.hw_params = avs_max98373_hw_params,
 };
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -106,8 +106,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -125,6 +123,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	    !dl->codecs[1].name || !dl->codecs[1].dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 2;
 	dl->platforms = platform;
@@ -149,18 +148,16 @@ static int avs_max98373_probe(struct platform_device *pdev)
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -190,10 +187,6 @@ static int avs_max98373_probe(struct platform_device *pdev)
 	card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
index a68e227..db07312 100644
--- a/sound/soc/intel/avs/boards/max98927.c
+++ b/sound/soc/intel/avs/boards/max98927.c
@@ -92,8 +92,8 @@ static const struct snd_soc_ops avs_max98927_ops = {
 	.hw_params = avs_max98927_hw_params,
 };
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -103,8 +103,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -122,6 +120,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	    !dl->codecs[1].name || !dl->codecs[1].dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 2;
 	dl->platforms = platform;
@@ -146,18 +145,16 @@ static int avs_max98927_probe(struct platform_device *pdev)
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -187,10 +184,6 @@ static int avs_max98927_probe(struct platform_device *pdev)
 	card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index 3fb1a5d..9ca400a 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -172,8 +172,8 @@ static const struct snd_soc_ops avs_nau8825_ops = {
 	.trigger = avs_nau8825_trigger,
 };
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -183,8 +183,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -199,6 +197,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -250,18 +249,16 @@ static int avs_nau8825_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct snd_soc_jack *jack;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -293,10 +290,6 @@ static int avs_nau8825_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, jack);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c
index 06c1f19f..73884f8 100644
--- a/sound/soc/intel/avs/boards/probe.c
+++ b/sound/soc/intel/avs/boards/probe.c
@@ -9,45 +9,54 @@
 #include <linux/device.h>
 #include <linux/module.h>
 #include <sound/soc.h>
-#include <sound/soc-acpi.h>
 
-SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
-SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI")));
-SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform")));
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+	struct snd_soc_dai_link *dl;
 
-static struct snd_soc_dai_link probe_mb_dai_links[] = {
-	{
-		.name = "Compress Probe Capture",
-		.nonatomic = 1,
-		SND_SOC_DAILINK_REG(probe_cp, dummy, platform),
-	},
-};
+	dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+	if (!dl)
+		return -ENOMEM;
+
+	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+	dl->platforms = devm_kzalloc(dev, sizeof(*dl->platforms), GFP_KERNEL);
+	if (!dl->cpus || !dl->platforms)
+		return -ENOMEM;
+
+	dl->name = "Compress Probe Capture";
+	dl->cpus->dai_name = "Probe Extraction CPU DAI";
+	dl->num_cpus = 1;
+	dl->codecs = &snd_soc_dummy_dlc;
+	dl->num_codecs = 1;
+	dl->platforms->name = dev_name(dev);
+	dl->num_platforms = 1;
+	dl->nonatomic = 1;
+
+	*links = dl;
+	*num_links = 1;
+	return 0;
+}
 
 static int avs_probe_mb_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct snd_soc_acpi_mach *mach;
 	struct snd_soc_card *card;
 	int ret;
 
-	mach = dev_get_platdata(dev);
-
 	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
 	if (!card)
 		return -ENOMEM;
 
+	ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+	if (ret)
+		return ret;
+
 	card->driver_name = "avs_probe_mb";
 	card->long_name = card->name = "AVS PROBE";
 	card->dev = dev;
 	card->owner = THIS_MODULE;
-	card->dai_link = probe_mb_dai_links;
-	card->num_links = ARRAY_SIZE(probe_mb_dai_links);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index ec53829..4055ecc 100644
--- a/sound/soc/intel/avs/boards/rt274.c
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -117,7 +117,7 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime)
 		return ret;
 	}
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	return 0;
 }
@@ -147,8 +147,8 @@ static int avs_rt274_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
 	return 0;
 }
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -158,8 +158,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -174,6 +172,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -214,18 +213,16 @@ static int avs_rt274_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct snd_soc_jack *jack;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -257,10 +254,6 @@ static int avs_rt274_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, jack);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 2566e97..4c9ac54 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -115,8 +115,8 @@ static const struct snd_soc_ops avs_rt286_ops = {
 	.hw_params = avs_rt286_hw_params,
 };
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -126,8 +126,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -142,6 +140,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -183,18 +182,16 @@ static int avs_rt286_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct snd_soc_jack *jack;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
@@ -227,10 +224,6 @@ static int avs_rt286_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, jack);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index 7be34c8..2d7a774 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -132,8 +132,8 @@ static const struct snd_soc_ops avs_rt298_ops = {
 	.hw_params = avs_rt298_hw_params,
 };
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -143,8 +143,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -159,6 +157,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -203,18 +202,16 @@ static int avs_rt298_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct snd_soc_jack *jack;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -246,10 +243,6 @@ static int avs_rt298_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, jack);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c
index 45f091f..00b99e36 100644
--- a/sound/soc/intel/avs/boards/rt5514.c
+++ b/sound/soc/intel/avs/boards/rt5514.c
@@ -84,8 +84,8 @@ static const struct snd_soc_ops avs_rt5514_ops = {
 	.hw_params = avs_rt5514_hw_params,
 };
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -95,8 +95,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -111,6 +109,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -136,18 +135,16 @@ static int avs_rt5514_probe(struct platform_device *pdev)
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -173,10 +170,6 @@ static int avs_rt5514_probe(struct platform_device *pdev)
 	card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c
index 706b84f..97d1fa9 100644
--- a/sound/soc/intel/avs/boards/rt5640.c
+++ b/sound/soc/intel/avs/boards/rt5640.c
@@ -67,7 +67,7 @@ static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime)
 		return ret;
 
 	snd_soc_component_set_jack(codec_dai->component, jack, NULL);
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	return 0;
 }
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
index 5164880..68fea32 100644
--- a/sound/soc/intel/avs/boards/rt5663.c
+++ b/sound/soc/intel/avs/boards/rt5663.c
@@ -134,8 +134,8 @@ static const struct snd_soc_ops avs_rt5663_ops = {
 };
 
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -145,8 +145,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -161,6 +159,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -202,18 +201,16 @@ static int avs_rt5663_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct rt5663_private *priv;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -245,10 +242,6 @@ static int avs_rt5663_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, priv);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
index 9677b9e..8186372 100644
--- a/sound/soc/intel/avs/boards/rt5682.c
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -204,8 +204,8 @@ avs_rt5682_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_param
 	return 0;
 }
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -215,8 +215,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -231,6 +229,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 1;
 	dl->platforms = platform;
@@ -272,7 +271,6 @@ static int avs_rt5682_probe(struct platform_device *pdev)
 	struct snd_soc_card *card;
 	struct snd_soc_jack *jack;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	if (pdev->id_entry && pdev->id_entry->driver_data)
@@ -282,14 +280,13 @@ static int avs_rt5682_probe(struct platform_device *pdev)
 	dev_dbg(dev, "avs_rt5682_quirk = %lx\n", avs_rt5682_quirk);
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -321,10 +318,6 @@ static int avs_rt5682_probe(struct platform_device *pdev)
 	card->fully_routed = true;
 	snd_soc_card_set_drvdata(card, jack);
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
index 3786eef..ae0e6e2 100644
--- a/sound/soc/intel/avs/boards/ssm4567.c
+++ b/sound/soc/intel/avs/boards/ssm4567.c
@@ -81,8 +81,8 @@ avs_ssm4567_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_para
 	return 0;
 }
 
-static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
-			       int tdm_slot, struct snd_soc_dai_link **dai_link)
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+			       struct snd_soc_dai_link **dai_link)
 {
 	struct snd_soc_dai_link_component *platform;
 	struct snd_soc_dai_link *dl;
@@ -92,8 +92,6 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	if (!dl || !platform)
 		return -ENOMEM;
 
-	platform->name = platform_name;
-
 	dl->name = devm_kasprintf(dev, GFP_KERNEL,
 				  AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
 	dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
@@ -111,6 +109,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
 	    !dl->codecs[1].name || !dl->codecs[1].dai_name)
 		return -ENOMEM;
 
+	platform->name = dev_name(dev);
 	dl->num_cpus = 1;
 	dl->num_codecs = 2;
 	dl->platforms = platform;
@@ -135,18 +134,16 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
 	struct avs_mach_pdata *pdata;
 	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	const char *pname;
 	int ssp_port, tdm_slot, ret;
 
 	mach = dev_get_platdata(dev);
-	pname = mach->mach_params.platform;
 	pdata = mach->pdata;
 
 	ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
 	if (ret)
 		return ret;
 
-	ret = avs_create_dai_link(dev, pname, ssp_port, tdm_slot, &dai_link);
+	ret = avs_create_dai_link(dev, ssp_port, tdm_slot, &dai_link);
 	if (ret) {
 		dev_err(dev, "Failed to create dai link: %d", ret);
 		return ret;
@@ -176,10 +173,6 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
 	card->num_dapm_routes = ARRAY_SIZE(card_base_routes);
 	card->fully_routed = true;
 
-	ret = snd_soc_fixup_dai_links_platform_name(card, pname);
-	if (ret)
-		return ret;
-
 	return devm_snd_soc_register_deferrable_card(dev, card);
 }
 
diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c
index 03f8fb0..5b5359e 100644
--- a/sound/soc/intel/avs/cnl.c
+++ b/sound/soc/intel/avs/cnl.c
@@ -8,6 +8,7 @@
 
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 #include "registers.h"
 
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 5ebadba..6e0e655 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -27,6 +27,7 @@
 #include "../../codecs/hda.h"
 #include "avs.h"
 #include "cldma.h"
+#include "debug.h"
 #include "messages.h"
 #include "pcm.h"
 
diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h
new file mode 100644
index 0000000..94fe872
--- /dev/null
+++ b/sound/soc/intel/avs/debug.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2024-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H
+#define __SOUND_SOC_INTEL_AVS_DEBUG_H
+
+#include "messages.h"
+#include "registers.h"
+
+struct avs_dev;
+
+#define avs_log_buffer_size(adev) \
+	((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({										\
+	s32 __offset = avs_dsp_op(adev, log_buffer_offset, core);		\
+	(__offset < 0) ? NULL :							\
+			 (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset);	\
+})
+
+static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&adev->trace_lock, flags);
+	ret = avs_dsp_op(adev, log_buffer_status, msg);
+	spin_unlock_irqrestore(&adev->trace_lock, flags);
+
+	return ret;
+}
+
+struct avs_apl_log_buffer_layout {
+	u32 read_ptr;
+	u32 write_ptr;
+	u8 buffer[];
+} __packed;
+static_assert(sizeof(struct avs_apl_log_buffer_layout) == 8);
+
+#define avs_apl_log_payload_size(adev) \
+	(avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout))
+
+#define avs_apl_log_payload_addr(addr) \
+	(addr + sizeof(struct avs_apl_log_buffer_layout))
+
+#ifdef CONFIG_DEBUG_FS
+int avs_register_probe_component(struct avs_dev *adev, const char *name);
+
+#define AVS_SET_ENABLE_LOGS_OP(name) \
+	.enable_logs = avs_##name##_enable_logs
+
+bool avs_logging_fw(struct avs_dev *adev);
+void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
+
+void avs_debugfs_init(struct avs_dev *adev);
+void avs_debugfs_exit(struct avs_dev *adev);
+
+#else
+static inline int avs_register_probe_component(struct avs_dev *adev, const char *name)
+{
+	return -EOPNOTSUPP;
+}
+
+#define AVS_SET_ENABLE_LOGS_OP(name)
+
+static inline bool avs_logging_fw(struct avs_dev *adev)
+{
+	return false;
+}
+
+static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len)
+{
+}
+
+static inline void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src,
+					  unsigned int len)
+{
+}
+
+static inline void avs_debugfs_init(struct avs_dev *adev) { }
+static inline void avs_debugfs_exit(struct avs_dev *adev) { }
+#endif
+
+#endif
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
index f508f21..3534de4 100644
--- a/sound/soc/intel/avs/debugfs.c
+++ b/sound/soc/intel/avs/debugfs.c
@@ -13,6 +13,7 @@
 #include <linux/string_helpers.h>
 #include <sound/soc.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 
 static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c
index f8d327e..d655e72 100644
--- a/sound/soc/intel/avs/icl.c
+++ b/sound/soc/intel/avs/icl.c
@@ -10,6 +10,7 @@
 #include <sound/hdaudio.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 
 #define ICL_VS_LTRP_GB_ICCMAX	95
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 6bfb9d1..c0feb9e 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -10,6 +10,7 @@
 #include <linux/slab.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 #include "registers.h"
 #include "trace.h"
diff --git a/sound/soc/intel/avs/lnl.c b/sound/soc/intel/avs/lnl.c
index 0320859..4fbc62b 100644
--- a/sound/soc/intel/avs/lnl.c
+++ b/sound/soc/intel/avs/lnl.c
@@ -8,6 +8,7 @@
 
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "registers.h"
 
 int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
diff --git a/sound/soc/intel/avs/mtl.c b/sound/soc/intel/avs/mtl.c
index e7b7915..d8bdd03 100644
--- a/sound/soc/intel/avs/mtl.c
+++ b/sound/soc/intel/avs/mtl.c
@@ -8,6 +8,7 @@
 
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "registers.h"
 #include "trace.h"
 
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index e8e6b1c..7aa20fc 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -115,6 +115,55 @@ avs_path_find_variant(struct avs_dev *adev,
 	return NULL;
 }
 
+static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev,
+						       struct avs_tplg_path_template *template,
+						       struct avs_path *source,
+						       struct avs_path *sink)
+{
+	struct avs_tplg_path *variant;
+
+	list_for_each_entry(variant, &template->path_list, node) {
+		if (variant->source_path_id == source->template->id &&
+		    variant->sink_path_id == sink->template->id)
+			return variant;
+	}
+
+	return NULL;
+}
+
+static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
+					    struct avs_tplg_path_template_id *id2)
+{
+	return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
+}
+
+static struct avs_path *avs_condpath_find_match(struct avs_dev *adev,
+						struct avs_tplg_path_template *template,
+						struct avs_path *path, int dir)
+{
+	struct avs_tplg_path_template_id *id, *id2;
+
+	if (dir) {
+		id = &template->source;
+		id2 = &template->sink;
+	} else {
+		id = &template->sink;
+		id2 = &template->source;
+	}
+
+	/* Check whether this path is either source or sink of condpath template. */
+	if (id->id != path->template->owner->id ||
+	    strcmp(id->tplg_name, path->template->owner->owner->name))
+		return NULL;
+
+	/* Unidirectional condpaths are allowed. */
+	if (avs_tplg_path_template_id_equal(id, id2))
+		return path;
+
+	/* Now find the counterpart. */
+	return avs_path_find_path(adev, id2->tplg_name, id2->id);
+}
+
 static struct acpi_nhlt_config *
 avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t);
 
@@ -1051,6 +1100,10 @@ static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
 	path->dma_id = dma_id;
 	INIT_LIST_HEAD(&path->ppl_list);
 	INIT_LIST_HEAD(&path->node);
+	INIT_LIST_HEAD(&path->source_list);
+	INIT_LIST_HEAD(&path->sink_list);
+	INIT_LIST_HEAD(&path->source_node);
+	INIT_LIST_HEAD(&path->sink_node);
 
 	/* create all the pipelines */
 	list_for_each_entry(tppl, &template->ppl_list, node) {
@@ -1134,12 +1187,129 @@ static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_i
 	return ERR_PTR(ret);
 }
 
+static void avs_condpath_free(struct avs_dev *adev, struct avs_path *path)
+{
+	int ret;
+
+	list_del(&path->source_node);
+	list_del(&path->sink_node);
+
+	ret = avs_path_reset(path);
+	if (ret < 0)
+		dev_err(adev->dev, "reset condpath failed: %d\n", ret);
+
+	ret = avs_path_unbind(path);
+	if (ret < 0)
+		dev_err(adev->dev, "unbind condpath failed: %d\n", ret);
+
+	avs_path_free_unlocked(path);
+}
+
+static struct avs_path *avs_condpath_create(struct avs_dev *adev,
+					    struct avs_tplg_path *template,
+					    struct avs_path *source,
+					    struct avs_path *sink)
+{
+	struct avs_path *path;
+	int ret;
+
+	path = avs_path_create_unlocked(adev, 0, template);
+	if (IS_ERR(path))
+		return path;
+
+	ret = avs_path_bind(path);
+	if (ret)
+		goto err_bind;
+
+	ret = avs_path_reset(path);
+	if (ret)
+		goto err_reset;
+
+	path->source = source;
+	path->sink = sink;
+	list_add_tail(&path->source_node, &source->source_list);
+	list_add_tail(&path->sink_node, &sink->sink_list);
+
+	return path;
+
+err_reset:
+	avs_path_unbind(path);
+err_bind:
+	avs_path_free_unlocked(path);
+	return ERR_PTR(ret);
+}
+
+static int avs_condpaths_walk(struct avs_dev *adev, struct avs_path *path, int dir)
+{
+	struct avs_soc_component *acomp;
+	struct avs_path *source, *sink;
+	struct avs_path **other;
+
+	if (dir) {
+		source = path;
+		other = &sink;
+	} else {
+		sink = path;
+		other = &source;
+	}
+
+	list_for_each_entry(acomp, &adev->comp_list, node) {
+		for (int i = 0; i < acomp->tplg->num_condpath_tmpls; i++) {
+			struct avs_tplg_path_template *template;
+			struct avs_tplg_path *variant;
+			struct avs_path *cpath;
+
+			template = &acomp->tplg->condpath_tmpls[i];
+
+			/* Do not create unidirectional condpaths twice. */
+			if (avs_tplg_path_template_id_equal(&template->source,
+							    &template->sink) && dir)
+				continue;
+
+			*other = avs_condpath_find_match(adev, template, path, dir);
+			if (!*other)
+				continue;
+
+			variant = avs_condpath_find_variant(adev, template, source, sink);
+			if (!variant)
+				continue;
+
+			cpath = avs_condpath_create(adev, variant, source, sink);
+			if (IS_ERR(cpath))
+				return PTR_ERR(cpath);
+		}
+	}
+
+	return 0;
+}
+
+/* Caller responsible for holding adev->path_mutex. */
+static int avs_condpaths_walk_all(struct avs_dev *adev, struct avs_path *path)
+{
+	int ret;
+
+	ret = avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_CAPTURE);
+	if (ret)
+		return ret;
+
+	return avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
 void avs_path_free(struct avs_path *path)
 {
+	struct avs_path *cpath, *csave;
 	struct avs_dev *adev = path->owner;
 
 	mutex_lock(&adev->path_mutex);
+
+	/* Free all condpaths this path spawned. */
+	list_for_each_entry_safe(cpath, csave, &path->source_list, source_node)
+		avs_condpath_free(path->owner, cpath);
+	list_for_each_entry_safe(cpath, csave, &path->sink_list, sink_node)
+		avs_condpath_free(path->owner, cpath);
+
 	avs_path_free_unlocked(path);
+
 	mutex_unlock(&adev->path_mutex);
 }
 
@@ -1150,6 +1320,7 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
 {
 	struct avs_tplg_path *variant;
 	struct avs_path *path;
+	int ret;
 
 	variant = avs_path_find_variant(adev, template, fe_params, be_params);
 	if (!variant) {
@@ -1163,7 +1334,16 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
 	mutex_lock(&adev->comp_list_mutex);
 
 	path = avs_path_create_unlocked(adev, dma_id, variant);
+	if (IS_ERR(path))
+		goto exit;
 
+	ret = avs_condpaths_walk_all(adev, path);
+	if (ret) {
+		avs_path_free_unlocked(path);
+		path = ERR_PTR(ret);
+	}
+
+exit:
 	mutex_unlock(&adev->comp_list_mutex);
 	mutex_unlock(&adev->path_mutex);
 
@@ -1286,6 +1466,42 @@ int avs_path_reset(struct avs_path *path)
 	return 0;
 }
 
+static int avs_condpath_pause(struct avs_dev *adev, struct avs_path *cpath)
+{
+	struct avs_path_pipeline *ppl;
+	int ret;
+
+	if (cpath->state == AVS_PPL_STATE_PAUSED)
+		return 0;
+
+	list_for_each_entry_reverse(ppl, &cpath->ppl_list, node) {
+		ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_PAUSED);
+		if (ret) {
+			dev_err(adev->dev, "pause cpath failed: %d\n", ret);
+			cpath->state = AVS_PPL_STATE_INVALID;
+			return AVS_IPC_RET(ret);
+		}
+	}
+
+	cpath->state = AVS_PPL_STATE_PAUSED;
+	return 0;
+}
+
+static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path)
+{
+	struct avs_path *cpath;
+
+	mutex_lock(&adev->path_mutex);
+
+	/* If either source or sink stops, so do the attached conditional paths. */
+	list_for_each_entry(cpath, &path->source_list, source_node)
+		avs_condpath_pause(adev, cpath);
+	list_for_each_entry(cpath, &path->sink_list, sink_node)
+		avs_condpath_pause(adev, cpath);
+
+	mutex_unlock(&adev->path_mutex);
+}
+
 int avs_path_pause(struct avs_path *path)
 {
 	struct avs_path_pipeline *ppl;
@@ -1295,6 +1511,8 @@ int avs_path_pause(struct avs_path *path)
 	if (path->state == AVS_PPL_STATE_PAUSED)
 		return 0;
 
+	avs_condpaths_pause(adev, path);
+
 	list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
 		ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
 						 AVS_PPL_STATE_PAUSED);
@@ -1309,6 +1527,50 @@ int avs_path_pause(struct avs_path *path)
 	return 0;
 }
 
+static int avs_condpath_run(struct avs_dev *adev, struct avs_path *cpath, int trigger)
+{
+	struct avs_path_pipeline *ppl;
+	int ret;
+
+	if (cpath->state == AVS_PPL_STATE_RUNNING)
+		return 0;
+
+	list_for_each_entry(ppl, &cpath->ppl_list, node) {
+		if (ppl->template->cfg->trigger != trigger)
+			continue;
+
+		ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_RUNNING);
+		if (ret) {
+			dev_err(adev->dev, "run cpath failed: %d\n", ret);
+			cpath->state = AVS_PPL_STATE_INVALID;
+			return AVS_IPC_RET(ret);
+		}
+	}
+
+	cpath->state = AVS_PPL_STATE_RUNNING;
+	return 0;
+}
+
+static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int trigger)
+{
+	struct avs_path *cpath;
+
+	mutex_lock(&adev->path_mutex);
+
+	/* Run conditional paths only if source and sink are both running. */
+	list_for_each_entry(cpath, &path->source_list, source_node)
+		if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+		    cpath->sink->state == AVS_PPL_STATE_RUNNING)
+			avs_condpath_run(adev, cpath, trigger);
+
+	list_for_each_entry(cpath, &path->sink_list, sink_node)
+		if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+		    cpath->sink->state == AVS_PPL_STATE_RUNNING)
+			avs_condpath_run(adev, cpath, trigger);
+
+	mutex_unlock(&adev->path_mutex);
+}
+
 int avs_path_run(struct avs_path *path, int trigger)
 {
 	struct avs_path_pipeline *ppl;
@@ -1332,5 +1594,10 @@ int avs_path_run(struct avs_path *path, int trigger)
 	}
 
 	path->state = AVS_PPL_STATE_RUNNING;
+
+	/* Granular pipeline triggering not intended for conditional paths. */
+	if (trigger == AVS_TPLG_TRIGGER_AUTO)
+		avs_condpaths_run(adev, path, trigger);
+
 	return 0;
 }
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
index c65ed84..ceb8997 100644
--- a/sound/soc/intel/avs/path.h
+++ b/sound/soc/intel/avs/path.h
@@ -13,11 +13,24 @@
 #include "avs.h"
 #include "topology.h"
 
+#define AVS_COND_TYPE_NONE	0
+#define AVS_COND_TYPE_AECREF	1
+
 struct avs_path {
 	u32 dma_id;
 	struct list_head ppl_list;
 	u32 state;
 
+	/* condpath navigation for standard paths */
+	struct list_head source_list;
+	struct list_head sink_list;
+
+	/* conditional path fields */
+	struct avs_path *source;
+	struct avs_path *sink;
+	struct list_head source_node;
+	struct list_head sink_node;
+
 	struct avs_tplg_path *template;
 	struct avs_dev *owner;
 	/* device path management */
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 67ce667..d31058e 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -1379,9 +1379,9 @@ static struct snd_soc_component_driver avs_component_driver = {
 	.topology_name_prefix	= "intel/avs",
 };
 
-int avs_soc_component_register(struct device *dev, const char *name,
-			       struct snd_soc_component_driver *drv,
-			       struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+int avs_register_component(struct device *dev, const char *name,
+			   struct snd_soc_component_driver *drv,
+			   struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
 {
 	struct avs_soc_component *acomp;
 	int ret;
@@ -1390,16 +1390,18 @@ int avs_soc_component_register(struct device *dev, const char *name,
 	if (!acomp)
 		return -ENOMEM;
 
-	ret = snd_soc_component_initialize(&acomp->base, drv, dev);
-	if (ret < 0)
-		return ret;
+	acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL);
+	if (!acomp->base.name)
+		return -ENOMEM;
 
-	/* force name change after ASoC is done with its init */
-	acomp->base.name = name;
 	INIT_LIST_HEAD(&acomp->node);
 
 	drv->use_dai_pcm_id = !obsolete_card_names;
 
+	ret = snd_soc_component_initialize(&acomp->base, drv, dev);
+	if (ret < 0)
+		return ret;
+
 	return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
 }
 
@@ -1426,7 +1428,7 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = {
 },
 };
 
-int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_dmic_component(struct avs_dev *adev, const char *name)
 {
 	const struct snd_soc_dai_ops *ops;
 
@@ -1437,8 +1439,8 @@ int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
 
 	dmic_cpu_dais[0].ops = ops;
 	dmic_cpu_dais[1].ops = ops;
-	return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
-					  ARRAY_SIZE(dmic_cpu_dais));
+	return avs_register_component(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
+				      ARRAY_SIZE(dmic_cpu_dais));
 }
 
 static const struct snd_soc_dai_driver i2s_dai_template = {
@@ -1470,8 +1472,8 @@ static const struct snd_soc_dai_driver i2s_dai_template = {
 	},
 };
 
-int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
-			      unsigned long *tdms)
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+			       unsigned long *tdms)
 {
 	struct snd_soc_dai_driver *cpus, *dai;
 	const struct snd_soc_dai_ops *ops;
@@ -1537,7 +1539,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
 	}
 
 plat_register:
-	return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+	return avs_register_component(adev->dev, name, &avs_component_driver, cpus, cpu_count);
 }
 
 /* HD-Audio CPU DAI template */
@@ -1762,8 +1764,7 @@ static struct snd_soc_component_driver avs_hda_component_driver = {
 	.topology_name_prefix	= "intel/avs",
 };
 
-int avs_hda_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_hda_component(struct avs_dev *adev, const char *name)
 {
-	return avs_soc_component_register(adev->dev, name,
-					  &avs_hda_component_driver, NULL, 0);
+	return avs_register_component(adev->dev, name, &avs_hda_component_driver, NULL, 0);
 }
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
index a42736b..c7b7000 100644
--- a/sound/soc/intel/avs/probes.c
+++ b/sound/soc/intel/avs/probes.c
@@ -11,6 +11,7 @@
 #include <sound/hdaudio.h>
 #include <sound/soc.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 
 static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
@@ -284,14 +285,28 @@ static struct snd_soc_dai_driver probe_cpu_dais[] = {
 },
 };
 
-static struct snd_soc_component_driver avs_probe_component_driver = {
+static const struct snd_soc_component_driver avs_probe_component_driver = {
 	.name			= "avs-probe-compr",
 	.compress_ops		= &avs_probe_compress_ops,
 	.module_get_upon_open	= 1, /* increment refcount when a stream is opened */
 };
 
-int avs_probe_platform_register(struct avs_dev *adev, const char *name)
+int avs_register_probe_component(struct avs_dev *adev, const char *name)
 {
-	return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
-					  probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
+	struct snd_soc_component *component;
+	int ret;
+
+	component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL);
+	if (!component)
+		return -ENOMEM;
+
+	component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL);
+	if (!component->name)
+		return -ENOMEM;
+
+	ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev);
+	if (ret)
+		return ret;
+
+	return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
 }
diff --git a/sound/soc/intel/avs/ptl.c b/sound/soc/intel/avs/ptl.c
index 2be4b54..07da9b0 100644
--- a/sound/soc/intel/avs/ptl.c
+++ b/sound/soc/intel/avs/ptl.c
@@ -8,6 +8,7 @@
 
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "debug.h"
 #include "registers.h"
 #include "trace.h"
 
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
index d66ef00..8fb86f3 100644
--- a/sound/soc/intel/avs/skl.c
+++ b/sound/soc/intel/avs/skl.c
@@ -11,6 +11,7 @@
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "cldma.h"
+#include "debug.h"
 #include "messages.h"
 #include "registers.h"
 
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
index 9dbb3ad..afb0665 100644
--- a/sound/soc/intel/avs/tgl.c
+++ b/sound/soc/intel/avs/tgl.c
@@ -8,6 +8,7 @@
 
 #include <linux/pci.h>
 #include "avs.h"
+#include "debug.h"
 #include "messages.h"
 
 #define CPUID_TSC_LEAF 0x15
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index f2e4ad8..dfe8cf5 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1387,6 +1387,27 @@ static const struct avs_tplg_token_parser path_parsers[] = {
 	},
 };
 
+static const struct avs_tplg_token_parser condpath_parsers[] = {
+	{
+		.token = AVS_TKN_CONDPATH_ID_U32,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+		.offset = offsetof(struct avs_tplg_path, id),
+		.parse = avs_parse_word_token,
+	},
+	{
+		.token = AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+		.offset = offsetof(struct avs_tplg_path, source_path_id),
+		.parse = avs_parse_word_token,
+	},
+	{
+		.token = AVS_TKN_CONDPATH_SINK_PATH_ID_U32,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+		.offset = offsetof(struct avs_tplg_path, sink_path_id),
+		.parse = avs_parse_word_token,
+	},
+};
+
 static struct avs_tplg_path *
 avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
 		     struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
@@ -1454,6 +1475,39 @@ static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
 	},
 };
 
+static const struct avs_tplg_token_parser condpath_tmpl_parsers[] = {
+	{
+		.token = AVS_TKN_CONDPATH_TMPL_ID_U32,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+		.offset = offsetof(struct avs_tplg_path_template, id),
+		.parse = avs_parse_word_token,
+	},
+	{
+		.token = AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+		.offset = offsetof(struct avs_tplg_path_template, source.tplg_name),
+		.parse = avs_parse_string_token,
+	},
+	{
+		.token = AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+		.offset = offsetof(struct avs_tplg_path_template, source.id),
+		.parse = avs_parse_word_token,
+	},
+	{
+		.token = AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+		.offset = offsetof(struct avs_tplg_path_template, sink.tplg_name),
+		.parse = avs_parse_string_token,
+	},
+	{
+		.token = AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32,
+		.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+		.offset = offsetof(struct avs_tplg_path_template, sink.id),
+		.parse = avs_parse_word_token,
+	},
+};
+
 static int parse_path_template(struct snd_soc_component *comp,
 			       struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
 			       struct avs_tplg_path_template *template,
@@ -1524,6 +1578,56 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o
 	return template;
 }
 
+static int avs_tplg_parse_condpath_templates(struct snd_soc_component *comp,
+					     struct snd_soc_tplg_vendor_array *tuples,
+					     u32 block_size)
+{
+	struct avs_soc_component *acomp = to_avs_soc_component(comp);
+	struct avs_tplg *tplg = acomp->tplg;
+	int ret, i;
+
+	ret = parse_dictionary_header(comp, tuples, (void **)&tplg->condpath_tmpls,
+				      &tplg->num_condpath_tmpls,
+				      sizeof(*tplg->condpath_tmpls),
+				      AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32);
+	if (ret)
+		return ret;
+
+	block_size -= le32_to_cpu(tuples->size);
+	/* With header parsed, move on to parsing entries. */
+	tuples = avs_tplg_vendor_array_next(tuples);
+
+	for (i = 0; i < tplg->num_condpath_tmpls; i++) {
+		struct avs_tplg_path_template *template;
+		u32 esize;
+
+		template = &tplg->condpath_tmpls[i];
+		template->owner = tplg; /* Used when building sysfs hierarchy. */
+		INIT_LIST_HEAD(&template->path_list);
+		INIT_LIST_HEAD(&template->node);
+
+		ret = avs_tplg_vendor_entry_size(tuples, block_size,
+						 AVS_TKN_CONDPATH_TMPL_ID_U32, &esize);
+		if (ret)
+			return ret;
+
+		ret = parse_path_template(comp, tuples, esize, template,
+					  condpath_tmpl_parsers,
+					  ARRAY_SIZE(condpath_tmpl_parsers),
+					  condpath_parsers,
+					  ARRAY_SIZE(condpath_parsers));
+		if (ret < 0) {
+			dev_err(comp->dev, "parse condpath_tmpl: %d failed: %d\n", i, ret);
+			return ret;
+		}
+
+		block_size -= esize;
+		tuples = avs_tplg_vendor_array_at(tuples, esize);
+	}
+
+	return 0;
+}
+
 static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
 	{
 		.token = AVS_TKN_INIT_CONFIG_ID_U32,
@@ -1891,6 +1995,12 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
 		return ret;
 	}
 
+	/* Condpaths dictionary. */
+	ret = avs_tplg_parse_condpath_templates(comp, tuples,
+						has_init_config ? offset : remaining);
+	if (ret < 0)
+		return ret;
+
 	if (!has_init_config)
 		return 0;
 
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index f5601a4..1e83fcc 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -33,6 +33,7 @@ struct avs_tplg {
 	u32 num_pplcfgs;
 	struct avs_tplg_binding *bindings;
 	u32 num_bindings;
+	struct avs_tplg_path_template *condpath_tmpls;
 	u32 num_condpath_tmpls;
 	struct avs_tplg_init_config *init_configs;
 	u32 num_init_configs;
@@ -155,6 +156,10 @@ struct avs_tplg_path_template {
 
 	struct snd_soc_dapm_widget *w;
 
+	/* Conditional path. */
+	struct avs_tplg_path_template_id source;
+	struct avs_tplg_path_template_id sink;
+
 	struct list_head path_list;
 
 	struct avs_tplg *owner;
@@ -176,6 +181,9 @@ struct avs_tplg_path {
 	/* Path format requirements. */
 	struct avs_audio_format *fe_fmt;
 	struct avs_audio_format *be_fmt;
+	/* Condpath path-variant requirements. */
+	u32 source_path_id;
+	u32 sink_path_id;
 
 	struct list_head ppl_list;
 
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 68a3d34..27b63a8 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -77,7 +77,7 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd)
 					   byt_cht_cx2072x_acpi_gpios))
 		dev_warn(rtd->dev, "Unable to add GPIO mapping table\n");
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	/* set the default PLL rate, the clock is handled by the codec driver */
 	ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), CX2072X_MCLK_EXTERNAL_PLL,
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 62594e7..3b5f631 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -47,7 +47,8 @@ enum {
 	BYT_CHT_ES8316_INTMIC_IN2_MAP,
 };
 
-#define BYT_CHT_ES8316_MAP(quirk)		((quirk) & GENMASK(3, 0))
+#define BYT_CHT_ES8316_MAP_MASK			GENMASK(3, 0)
+#define BYT_CHT_ES8316_MAP(quirk)		((quirk) & BYT_CHT_ES8316_MAP_MASK)
 #define BYT_CHT_ES8316_SSP0			BIT(16)
 #define BYT_CHT_ES8316_MONO_SPEAKER		BIT(17)
 #define BYT_CHT_ES8316_JD_INVERTED		BIT(18)
@@ -60,10 +61,23 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override");
 
 static void log_quirks(struct device *dev)
 {
-	if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP)
+	int map;
+
+	map = BYT_CHT_ES8316_MAP(quirk);
+	switch (map) {
+	case BYT_CHT_ES8316_INTMIC_IN1_MAP:
 		dev_info(dev, "quirk IN1_MAP enabled");
-	if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP)
+		break;
+	case BYT_CHT_ES8316_INTMIC_IN2_MAP:
 		dev_info(dev, "quirk IN2_MAP enabled");
+		break;
+	default:
+		dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to INTMIC_IN1_MAP\n", map);
+		quirk &= ~BYT_CHT_ES8316_MAP_MASK;
+		quirk |= BYT_CHT_ES8316_INTMIC_IN1_MAP;
+		break;
+	}
+
 	if (quirk & BYT_CHT_ES8316_SSP0)
 		dev_info(dev, "quirk SSP0 enabled");
 	if (quirk & BYT_CHT_ES8316_MONO_SPEAKER)
@@ -165,7 +179,7 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
 	int num_routes;
 	int ret;
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	switch (BYT_CHT_ES8316_MAP(quirk)) {
 	case BYT_CHT_ES8316_INTMIC_IN1_MAP:
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 0f3b8f4..1e9b1903 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -68,7 +68,8 @@ enum {
 	BYT_RT5640_OVCD_SF_1P5		= (RT5640_OVCD_SF_1P5 << 13),
 };
 
-#define BYT_RT5640_MAP(quirk)		((quirk) &  GENMASK(3, 0))
+#define BYT_RT5640_MAP_MASK		GENMASK(3, 0)
+#define BYT_RT5640_MAP(quirk)		((quirk) & BYT_RT5640_MAP_MASK)
 #define BYT_RT5640_JDSRC(quirk)		(((quirk) & GENMASK(7, 4)) >> 4)
 #define BYT_RT5640_OVCD_TH(quirk)	(((quirk) & GENMASK(12, 8)) >> 8)
 #define BYT_RT5640_OVCD_SF(quirk)	(((quirk) & GENMASK(14, 13)) >> 13)
@@ -140,7 +141,9 @@ static void log_quirks(struct device *dev)
 		dev_info(dev, "quirk NO_INTERNAL_MIC_MAP enabled\n");
 		break;
 	default:
-		dev_err(dev, "quirk map 0x%x is not supported, microphone input will not work\n", map);
+		dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to DMIC1_MAP\n", map);
+		byt_rt5640_quirk &= ~BYT_RT5640_MAP_MASK;
+		byt_rt5640_quirk |= BYT_RT5640_DMIC1_MAP;
 		break;
 	}
 	if (byt_rt5640_quirk & BYT_RT5640_HSMIC2_ON_IN1)
@@ -1321,7 +1324,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
 	int num_routes = 0;
 	int ret;
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 	jack_data->use_platform_clock = true;
 
 	/* Start with RC clk for jack-detect (we disable MCLK below) */
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 67c6284..ca540a6 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -58,7 +58,8 @@ enum {
 	BYT_RT5651_OVCD_SF_1P5	= (RT5651_OVCD_SF_1P5 << 13),
 };
 
-#define BYT_RT5651_MAP(quirk)		((quirk) & GENMASK(3, 0))
+#define BYT_RT5651_MAP_MASK		GENMASK(3, 0)
+#define BYT_RT5651_MAP(quirk)		((quirk) & BYT_RT5651_MAP_MASK)
 #define BYT_RT5651_JDSRC(quirk)		(((quirk) & GENMASK(7, 4)) >> 4)
 #define BYT_RT5651_OVCD_TH(quirk)	(((quirk) & GENMASK(12, 8)) >> 8)
 #define BYT_RT5651_OVCD_SF(quirk)	(((quirk) & GENMASK(14, 13)) >> 13)
@@ -100,14 +101,29 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override");
 
 static void log_quirks(struct device *dev)
 {
-	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP)
+	int map;
+
+	map = BYT_RT5651_MAP(byt_rt5651_quirk);
+	switch (map) {
+	case BYT_RT5651_DMIC_MAP:
 		dev_info(dev, "quirk DMIC_MAP enabled");
-	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP)
+		break;
+	case BYT_RT5651_IN1_MAP:
 		dev_info(dev, "quirk IN1_MAP enabled");
-	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP)
+		break;
+	case BYT_RT5651_IN2_MAP:
 		dev_info(dev, "quirk IN2_MAP enabled");
-	if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_IN2_MAP)
+		break;
+	case BYT_RT5651_IN1_IN2_MAP:
 		dev_info(dev, "quirk IN1_IN2_MAP enabled");
+		break;
+	default:
+		dev_warn_once(dev, "quirk sets invalid input map: 0x%x, default to DMIC_MAP\n", map);
+		byt_rt5651_quirk &= ~BYT_RT5651_MAP_MASK;
+		byt_rt5651_quirk |= BYT_RT5651_DMIC_MAP;
+		break;
+	}
+
 	if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) {
 		dev_info(dev, "quirk realtek,jack-detect-source %ld\n",
 			 BYT_RT5651_JDSRC(byt_rt5651_quirk));
@@ -570,7 +586,7 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
 	int report;
 	int ret;
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	/* Start with RC clk for jack-detect (we disable MCLK below) */
 	if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN)
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index a6dfbcf..72c0e59 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -288,7 +288,7 @@ static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
 	const struct snd_soc_dapm_route *custom_map = NULL;
 	int ret, jack_type, num_routes = 0;
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	ret = snd_soc_add_card_controls(card, byt_wm5102_controls,
 					ARRAY_SIZE(byt_wm5102_controls));
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index 1211a2b..10b189e 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -276,7 +276,7 @@ static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime)
 	int num_routes;
 	int ret;
 
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	if (quirk & SOC_ES8336_HEADSET_MIC1) {
 		custom_map = sof_es8316_headset_mic1_map;
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c
index 95a0839..a2a30a8 100644
--- a/sound/soc/mediatek/common/mtk-soundcard-driver.c
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c
@@ -89,40 +89,31 @@ static int set_dailink_daifmt(struct snd_soc_card *card,
 int parse_dai_link_info(struct snd_soc_card *card)
 {
 	struct device *dev = card->dev;
-	struct device_node *sub_node;
 	struct snd_soc_dai_link *dai_link;
 	const char *dai_link_name;
 	int ret, i;
 
 	/* Loop over all the dai link sub nodes */
-	for_each_available_child_of_node(dev->of_node, sub_node) {
+	for_each_available_child_of_node_scoped(dev->of_node, sub_node) {
 		if (of_property_read_string(sub_node, "link-name",
-					    &dai_link_name)) {
-			of_node_put(sub_node);
+					    &dai_link_name))
 			return -EINVAL;
-		}
 
 		for_each_card_prelinks(card, i, dai_link) {
 			if (!strcmp(dai_link_name, dai_link->name))
 				break;
 		}
 
-		if (i >= card->num_links) {
-			of_node_put(sub_node);
+		if (i >= card->num_links)
 			return -EINVAL;
-		}
 
 		ret = set_card_codec_info(card, sub_node, dai_link);
-		if (ret < 0) {
-			of_node_put(sub_node);
+		if (ret < 0)
 			return ret;
-		}
 
 		ret = set_dailink_daifmt(card, sub_node, dai_link);
-		if (ret < 0) {
-			of_node_put(sub_node);
+		if (ret < 0)
 			return ret;
-		}
 	}
 
 	return 0;
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 7d6a358..3d6d7bc 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -159,7 +159,7 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 {
 	int ret;
 
-	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
 				    &mt8173_rt5650_hdmi_jack);
 	if (ret)
 		return ret;
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
index 3388e07..983f3b9 100644
--- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -378,7 +378,7 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 		snd_soc_card_get_drvdata(rtd->card);
 	int ret;
 
-	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
 				    &priv->hdmi_jack);
 	if (ret)
 		return ret;
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
index 497a904..0bc1f11 100644
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -383,7 +383,7 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 		snd_soc_card_get_drvdata(rtd->card);
 	int ret;
 
-	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
 				    &priv->hdmi_jack);
 	if (ret)
 		return ret;
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c
index 4354601..45df698 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c
@@ -362,7 +362,7 @@ static int mt8186_mt6366_rt1019_rt5682s_hdmi_init(struct snd_soc_pcm_runtime *rt
 		return ret;
 	}
 
-	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack);
+	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
 	if (ret) {
 		dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
 		return ret;
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index ea814a0..c6e7461 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -250,14 +250,14 @@ enum mt8188_jacks {
 static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = {
 	{
 		.pin = "HDMI",
-		.mask = SND_JACK_LINEOUT,
+		.mask = SND_JACK_AVOUT,
 	},
 };
 
 static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = {
 	{
 		.pin = "DP",
-		.mask = SND_JACK_LINEOUT,
+		.mask = SND_JACK_AVOUT,
 	},
 };
 
@@ -638,7 +638,7 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
 	int ret = 0;
 
 	ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack",
-					 SND_JACK_LINEOUT, jack,
+					 SND_JACK_AVOUT, jack,
 					 mt8188_hdmi_jack_pins,
 					 ARRAY_SIZE(mt8188_hdmi_jack_pins));
 	if (ret) {
@@ -663,7 +663,7 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
 	struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
 	int ret = 0;
 
-	ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT,
+	ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_AVOUT,
 					 jack, mt8188_dp_jack_pins,
 					 ARRAY_SIZE(mt8188_dp_jack_pins));
 	if (ret) {
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
index bf483a8..91c5776 100644
--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -368,7 +368,7 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 		snd_soc_rtd_to_codec(rtd, 0)->component;
 	int ret;
 
-	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack);
+	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
 	if (ret) {
 		dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret);
 		return ret;
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
index e57391c..7b96c84 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -360,7 +360,7 @@ static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
 		snd_soc_rtd_to_codec(rtd, 0)->component;
 	int ret;
 
-	ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, jack);
+	ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_AVOUT, jack);
 	if (ret)
 		return ret;
 
@@ -375,7 +375,7 @@ static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
 		snd_soc_rtd_to_codec(rtd, 0)->component;
 	int ret;
 
-	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack);
+	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack);
 	if (ret)
 		return ret;
 
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index e026f99..e54abcd 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -3,7 +3,7 @@
 
 config SND_PXA2XX_SOC
 	tristate "SoC Audio for the Intel PXA2xx chip"
-	depends on ARCH_PXA || COMPILE_TEST
+	depends on ARCH_PXA || (COMPILE_TEST && GPIOLIB_LEGACY)
 	select SND_PXA2XX_LIB
 	help
 	  Say Y or M if you want to add support for codecs attached to
@@ -26,7 +26,7 @@
 
 config SND_PXA_SOC_SSP
 	tristate "Soc Audio via PXA2xx/PXA3xx SSP ports"
-	depends on PLAT_PXA
+	depends on ARCH_PXA
 	select PXA_SSP
 	select SND_PXA2XX_LIB
 
diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
index 8106c58..2dc8c75 100644
--- a/sound/soc/qcom/lpass-cdc-dma.c
+++ b/sound/soc/qcom/lpass-cdc-dma.c
@@ -217,8 +217,9 @@ static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
 	struct lpaif_dmactl *dmactl = NULL;
-	unsigned int ret, regval;
+	unsigned int regval;
 	unsigned int channels = params_channels(params);
+	int ret;
 	int id;
 
 	switch (channels) {
diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c
index ce753eb..6d97953 100644
--- a/sound/soc/qcom/lpass-hdmi.c
+++ b/sound/soc/qcom/lpass-hdmi.c
@@ -23,7 +23,6 @@ static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
 	snd_pcm_format_t format = params_format(params);
 	unsigned int rate = params_rate(params);
 	unsigned int channels = params_channels(params);
-	unsigned int ret;
 	int bitwidth;
 	unsigned int word_length;
 	unsigned int ch_sts_buf0;
@@ -33,6 +32,7 @@ static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
 	unsigned int ch = 0;
 	struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
 	struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
+	int ret;
 
 	bitwidth = snd_pcm_format_width(format);
 	if (bitwidth < 0) {
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index 4ebaaf7..bbfd51d 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -811,6 +811,30 @@ static int audioreach_gapless_set_media_format(struct q6apm_graph *graph,
 					 EARLY_EOS_DELAY_MS);
 }
 
+static int audioreach_set_module_config(struct q6apm_graph *graph,
+					struct audioreach_module *module,
+					struct audioreach_module_config *cfg)
+{
+	int payload_size = le32_to_cpu(module->data->size);
+	struct gpr_pkt *pkt;
+	int rc;
+	void *p;
+
+	pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+	if (IS_ERR(pkt))
+		return PTR_ERR(pkt);
+
+	p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+	memcpy(p, module->data->data, payload_size);
+
+	rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+	kfree(pkt);
+
+	return rc;
+}
+
 static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
 					   struct audioreach_module *module,
 					   struct audioreach_module_config *cfg)
@@ -1247,6 +1271,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod
 	case MODULE_ID_DISPLAY_PORT_SINK:
 		rc = audioreach_display_port_set_media_format(graph, module, cfg);
 		break;
+	case  MODULE_ID_SMECNS_V2:
+		rc = audioreach_set_module_config(graph, module, cfg);
+		break;
 	case MODULE_ID_I2S_SOURCE:
 	case MODULE_ID_I2S_SINK:
 		rc = audioreach_i2s_set_media_format(graph, module, cfg);
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 61a69df..790fba9 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -4,6 +4,7 @@
 #define __AUDIOREACH_H__
 #include <linux/types.h>
 #include <linux/soc/qcom/apr.h>
+#include <uapi/sound/snd_ar_tokens.h>
 #include <sound/soc.h>
 struct q6apm;
 struct q6apm_graph;
@@ -17,15 +18,16 @@ struct q6apm_graph;
 #define MODULE_ID_PCM_DEC		0x07001005
 #define MODULE_ID_PLACEHOLDER_ENCODER	0x07001008
 #define MODULE_ID_PLACEHOLDER_DECODER	0x07001009
-#define MODULE_ID_SAL			0x07001010
-#define MODULE_ID_MFC			0x07001015
-#define MODULE_ID_CODEC_DMA_SINK	0x07001023
-#define MODULE_ID_CODEC_DMA_SOURCE	0x07001024
 #define MODULE_ID_I2S_SINK		0x0700100A
 #define MODULE_ID_I2S_SOURCE		0x0700100B
+#define MODULE_ID_SAL			0x07001010
+#define MODULE_ID_MFC			0x07001015
 #define MODULE_ID_DATA_LOGGING		0x0700101A
 #define MODULE_ID_AAC_DEC		0x0700101F
+#define MODULE_ID_CODEC_DMA_SINK	0x07001023
+#define MODULE_ID_CODEC_DMA_SOURCE	0x07001024
 #define MODULE_ID_FLAC_DEC		0x0700102F
+#define MODULE_ID_SMECNS_V2		0x07001031
 #define MODULE_ID_MP3_DECODE		0x0700103B
 #define MODULE_ID_GAPLESS		0x0700104D
 #define MODULE_ID_DISPLAY_PORT_SINK	0x07001069
@@ -461,8 +463,8 @@ struct param_id_i2s_intf_cfg {
 } __packed;
 
 #define I2S_INTF_TYPE_PRIMARY		0
-#define I2S_INTF_TYPE_SECOINDARY	1
-#define I2S_INTF_TYPE_TERTINARY		2
+#define I2S_INTF_TYPE_SECONDARY		1
+#define I2S_INTF_TYPE_TERTIARY		2
 #define I2S_INTF_TYPE_QUATERNARY	3
 #define I2S_INTF_TYPE_QUINARY		4
 #define I2S_SD0				1
@@ -707,9 +709,6 @@ struct audioreach_module {
 	uint32_t max_ip_port;
 	uint32_t max_op_port;
 
-	uint32_t in_port;
-	uint32_t out_port;
-
 	uint32_t num_connections;
 	/* Connections */
 	uint32_t src_mod_inst_id;
@@ -745,6 +744,7 @@ struct audioreach_module {
 	struct list_head node;
 	struct audioreach_container *container;
 	struct snd_soc_dapm_widget *widget;
+	struct audioreach_module_priv_data *data;
 };
 
 struct audioreach_module_config {
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index 83319a9..f61285e 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -305,6 +305,34 @@ static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(
 	return NULL;
 }
 
+static struct audioreach_module_priv_data *audioreach_get_module_priv_data(
+		struct snd_soc_tplg_private *private)
+{
+	int sz;
+
+	for (sz = 0; sz < le32_to_cpu(private->size); ) {
+		struct snd_soc_tplg_vendor_array *mod_array;
+
+		mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
+		if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
+			struct audioreach_module_priv_data *pdata;
+
+			pdata = kzalloc(struct_size(pdata, data, le32_to_cpu(mod_array->size)),
+				       GFP_KERNEL);
+			if (!pdata)
+				return ERR_PTR(-ENOMEM);
+
+			memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data,
+						le32_to_cpu(mod_array->size)));
+			return pdata;
+		}
+
+		sz = sz + le32_to_cpu(mod_array->size);
+	}
+
+	return NULL;
+}
+
 static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,
 						       struct snd_soc_tplg_private *private)
 {
@@ -412,7 +440,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
 							struct snd_soc_tplg_private *private,
 							struct snd_soc_dapm_widget *w)
 {
-	uint32_t max_ip_port = 0, max_op_port = 0, in_port = 0, out_port = 0;
+	uint32_t max_ip_port = 0, max_op_port = 0;
 	uint32_t src_mod_op_port_id[AR_MAX_MOD_LINKS] = { 0, };
 	uint32_t dst_mod_inst_id[AR_MAX_MOD_LINKS] = { 0, };
 	uint32_t dst_mod_ip_port_id[AR_MAX_MOD_LINKS] = { 0, };
@@ -455,12 +483,6 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
 		case AR_TKN_U32_MODULE_MAX_OP_PORTS:
 			max_op_port = le32_to_cpu(mod_elem->value);
 			break;
-		case AR_TKN_U32_MODULE_IN_PORTS:
-			in_port = le32_to_cpu(mod_elem->value);
-			break;
-		case AR_TKN_U32_MODULE_OUT_PORTS:
-			out_port = le32_to_cpu(mod_elem->value);
-			break;
 		case AR_TKN_U32_MODULE_SRC_INSTANCE_ID:
 			src_mod_inst_id = le32_to_cpu(mod_elem->value);
 			break;
@@ -550,8 +572,6 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
 		mod->module_id = module_id;
 		mod->max_ip_port = max_ip_port;
 		mod->max_op_port = max_op_port;
-		mod->in_port = in_port;
-		mod->out_port = out_port;
 		mod->src_mod_inst_id = src_mod_inst_id;
 		for (pn = 0; pn < mod->max_op_port; pn++) {
 			if (src_mod_op_port_id[pn] && dst_mod_inst_id[pn] &&
@@ -587,8 +607,10 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon
 		return PTR_ERR(cont);
 
 	mod = audioreach_parse_common_tokens(apm, cont, &tplg_w->priv, w);
-	if (IS_ERR(mod))
-		return PTR_ERR(mod);
+	if (IS_ERR_OR_NULL(mod))
+		return mod ? PTR_ERR(mod) : -ENODEV;
+
+	mod->data = audioreach_get_module_priv_data(&tplg_w->priv);
 
 	dobj = &w->dobj;
 	dobj->private = mod;
@@ -947,6 +969,7 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp,
 	cont->num_modules--;
 
 	list_del(&mod->node);
+	kfree(mod->data);
 	kfree(mod);
 	/* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */
 	if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 73f9f82..5d10b1c 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -7,6 +7,7 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <linux/soundwire/sdw.h>
 #include <sound/jack.h>
 #include <linux/input-event-codes.h>
@@ -82,8 +83,10 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 					SNDRV_PCM_HW_PARAM_RATE);
 	struct snd_interval *channels = hw_param_interval(params,
 					SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
 
 	rate->min = rate->max = 48000;
+	snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
 	channels->min = 2;
 	channels->max = 2;
 	switch (cpu_dai->id) {
diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c
index 8eb57fc..444f216 100644
--- a/sound/soc/qcom/x1e80100.c
+++ b/sound/soc/qcom/x1e80100.c
@@ -210,14 +210,15 @@ static int x1e80100_platform_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	card->driver_name = "x1e80100";
+	card->driver_name = of_device_get_match_data(dev);
 	x1e80100_add_be_ops(card);
 
 	return devm_snd_soc_register_card(dev, card);
 }
 
 static const struct of_device_id snd_x1e80100_dt_match[] = {
-	{ .compatible = "qcom,x1e80100-sndcard", },
+	{ .compatible = "qcom,x1e80100-sndcard", .data = "x1e80100" },
+	{ .compatible = "qcom,glymur-sndcard", .data = "glymur" },
 	{}
 };
 MODULE_DEVICE_TABLE(of, snd_x1e80100_dt_match);
diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c
index 221ce91..630c2f5 100644
--- a/sound/soc/renesas/fsi.c
+++ b/sound/soc/renesas/fsi.c
@@ -343,14 +343,9 @@ static void __fsi_reg_mask_set(u32 __iomem *reg, u32 mask, u32 data)
 #define fsi_core_read(p, r)   _fsi_master_read(p, p->core->r)
 static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
 {
-	u32 ret;
-	unsigned long flags;
+	guard(spinlock_irqsave)(&master->lock);
 
-	spin_lock_irqsave(&master->lock, flags);
-	ret = __fsi_reg_read(master->base + reg);
-	spin_unlock_irqrestore(&master->lock, flags);
-
-	return ret;
+	return __fsi_reg_read(master->base + reg);
 }
 
 #define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d)
@@ -358,11 +353,9 @@ static u32 _fsi_master_read(struct fsi_master *master, u32 reg)
 static void _fsi_master_mask_set(struct fsi_master *master,
 			       u32 reg, u32 mask, u32 data)
 {
-	unsigned long flags;
+	guard(spinlock_irqsave)(&master->lock);
 
-	spin_lock_irqsave(&master->lock, flags);
 	__fsi_reg_mask_set(master->base + reg, mask, data);
-	spin_unlock_irqrestore(&master->lock, flags);
 }
 
 /*
@@ -499,14 +492,10 @@ static int fsi_stream_is_working(struct fsi_priv *fsi,
 				 struct fsi_stream *io)
 {
 	struct fsi_master *master = fsi_get_master(fsi);
-	unsigned long flags;
-	int ret;
 
-	spin_lock_irqsave(&master->lock, flags);
-	ret = !!(io->substream && io->substream->runtime);
-	spin_unlock_irqrestore(&master->lock, flags);
+	guard(spinlock_irqsave)(&master->lock);
 
-	return ret;
+	return !!(io->substream && io->substream->runtime);
 }
 
 static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io)
@@ -520,9 +509,9 @@ static void fsi_stream_init(struct fsi_priv *fsi,
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct fsi_master *master = fsi_get_master(fsi);
-	unsigned long flags;
 
-	spin_lock_irqsave(&master->lock, flags);
+	guard(spinlock_irqsave)(&master->lock);
+
 	io->substream	= substream;
 	io->buff_sample_capa	= fsi_frame2sample(fsi, runtime->buffer_size);
 	io->buff_sample_pos	= 0;
@@ -533,16 +522,14 @@ static void fsi_stream_init(struct fsi_priv *fsi,
 	io->oerr_num	= -1; /* ignore 1st err */
 	io->uerr_num	= -1; /* ignore 1st err */
 	fsi_stream_handler_call(io, init, fsi, io);
-	spin_unlock_irqrestore(&master->lock, flags);
 }
 
 static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
 {
 	struct snd_soc_dai *dai = fsi_get_dai(io->substream);
 	struct fsi_master *master = fsi_get_master(fsi);
-	unsigned long flags;
 
-	spin_lock_irqsave(&master->lock, flags);
+	guard(spinlock_irqsave)(&master->lock);
 
 	if (io->oerr_num > 0)
 		dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
@@ -560,7 +547,6 @@ static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io)
 	io->bus_option		= 0;
 	io->oerr_num	= 0;
 	io->uerr_num	= 0;
-	spin_unlock_irqrestore(&master->lock, flags);
 }
 
 static int fsi_stream_transfer(struct fsi_stream *io)
diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c
index 9f086906a..69fb199 100644
--- a/sound/soc/renesas/rcar/core.c
+++ b/sound/soc/renesas/rcar/core.c
@@ -696,25 +696,21 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
 	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
 	int ret;
-	unsigned long flags;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	guard(spinlock_irqsave)(&priv->lock);
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 		ret = rsnd_dai_call(init, io, priv);
 		if (ret < 0)
-			goto dai_trigger_end;
+			break;
 
 		ret = rsnd_dai_call(start, io, priv);
 		if (ret < 0)
-			goto dai_trigger_end;
+			break;
 
 		ret = rsnd_dai_call(irq, io, priv, 1);
-		if (ret < 0)
-			goto dai_trigger_end;
-
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -729,9 +725,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 		ret = -EINVAL;
 	}
 
-dai_trigger_end:
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return ret;
 }
 
@@ -1545,15 +1538,14 @@ static int rsnd_hw_update(struct snd_pcm_substream *substream,
 	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
 	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
 	struct rsnd_priv *priv = rsnd_io_to_priv(io);
-	unsigned long flags;
 	int ret;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	guard(spinlock_irqsave)(&priv->lock);
+
 	if (hw_params)
 		ret = rsnd_dai_call(hw_params, io, substream, hw_params);
 	else
 		ret = rsnd_dai_call(hw_free, io, substream);
-	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return ret;
 }
diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c
index 36d31ab..f5338bb 100644
--- a/sound/soc/renesas/rcar/msiof.c
+++ b/sound/soc/renesas/rcar/msiof.c
@@ -37,7 +37,6 @@
 /* SISTR */
 #define SISTR_ERR_TX	(SISTR_TFSERR | SISTR_TFOVF | SISTR_TFUDF)
 #define SISTR_ERR_RX	(SISTR_RFSERR | SISTR_RFOVF | SISTR_RFUDF)
-#define SISTR_ERR	(SISTR_ERR_TX | SISTR_ERR_RX)
 
 /*
  * The data on memory in 24bit case is located at <right> side
@@ -80,15 +79,19 @@ struct msiof_priv {
 #define msiof_is_play(substream)	((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK)
 #define msiof_read(priv, reg)		ioread32((priv)->base + reg)
 #define msiof_write(priv, reg, val)	iowrite32(val, (priv)->base + reg)
-#define msiof_status_clear(priv)	msiof_write(priv, SISTR, SISTR_ERR)
 
-static void msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val)
+static int msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val)
 {
 	u32 old = msiof_read(priv, reg);
 	u32 new = (old & ~mask) | (val & mask);
+	int updated = false;
 
-	if (old != new)
+	if (old != new) {
 		msiof_write(priv, reg, new);
+		updated = true;
+	}
+
+	return updated;
 }
 
 static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u32 val, u32 expect)
@@ -96,7 +99,9 @@ static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u3
 	u32 data;
 	int ret;
 
-	msiof_update(priv, reg, mask, val);
+	ret = msiof_update(priv, reg, mask, val);
+	if (!ret) /* no update */
+		return;
 
 	ret = readl_poll_timeout_atomic(priv->base + reg, data,
 					(data & mask) == expect, 1, 128);
@@ -131,6 +136,9 @@ static int msiof_hw_start(struct snd_soc_component *component,
 	priv->err_ovf[substream->stream] =
 	priv->err_udf[substream->stream] = 0;
 
+	/* Start DMAC */
+	snd_dmaengine_pcm_trigger(substream, cmd);
+
 	/* SITMDRx */
 	if (is_play) {
 		val = SITMDR1_PCON |
@@ -167,6 +175,13 @@ static int msiof_hw_start(struct snd_soc_component *component,
 		val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX;
 	msiof_update(priv, SIIER, val, val);
 
+	/* clear status */
+	if (is_play)
+		val = SISTR_ERR_TX;
+	else
+		val = SISTR_ERR_RX;
+	msiof_update(priv, SISTR, val, val);
+
 	/* SICTR */
 	if (is_play)
 		val = SICTR_TXE | SICTR_TEDG;
@@ -174,11 +189,6 @@ static int msiof_hw_start(struct snd_soc_component *component,
 		val = SICTR_RXE | SICTR_REDG;
 	msiof_update_and_wait(priv, SICTR, val, val, val);
 
-	msiof_status_clear(priv);
-
-	/* Start DMAC */
-	snd_dmaengine_pcm_trigger(substream, cmd);
-
 	return 0;
 }
 
@@ -211,7 +221,7 @@ static int msiof_hw_stop(struct snd_soc_component *component,
 	if (priv->err_syc[substream->stream] ||
 	    priv->err_ovf[substream->stream] ||
 	    priv->err_udf[substream->stream])
-		dev_warn(dev, "FSERR(%s) = %d, FOVF = %d, FUDF = %d\n",
+		dev_warn(dev, "%s: FSERR = %d, FOVF = %d, FUDF = %d\n",
 			 snd_pcm_direction_name(substream->stream),
 			 priv->err_syc[substream->stream],
 			 priv->err_ovf[substream->stream],
@@ -362,10 +372,9 @@ static int msiof_trigger(struct snd_soc_component *component,
 {
 	struct device *dev = component->dev;
 	struct msiof_priv *priv = dev_get_drvdata(dev);
-	unsigned long flags;
 	int ret = -EINVAL;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	guard(spinlock_irqsave)(&priv->lock);
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
@@ -382,8 +391,6 @@ static int msiof_trigger(struct snd_soc_component *component,
 		break;
 	}
 
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return ret;
 }
 
@@ -394,23 +401,18 @@ static int msiof_hw_params(struct snd_soc_component *component,
 	struct msiof_priv *priv = dev_get_drvdata(component->dev);
 	struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
 	struct dma_slave_config cfg = {};
-	unsigned long flags;
 	int ret;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	guard(spinlock_irqsave)(&priv->lock);
 
 	ret = snd_hwparams_to_dma_slave_config(substream, params, &cfg);
 	if (ret < 0)
-		goto hw_params_out;
+		return ret;
 
 	cfg.dst_addr = priv->phy_addr + SITFDR;
 	cfg.src_addr = priv->phy_addr + SIRFDR;
 
-	ret = dmaengine_slave_config(chan, &cfg);
-hw_params_out:
-	spin_unlock_irqrestore(&priv->lock, flags);
-
-	return ret;
+	return dmaengine_slave_config(chan, &cfg);
 }
 
 static const struct snd_soc_component_driver msiof_component_driver = {
@@ -429,12 +431,10 @@ static irqreturn_t msiof_interrupt(int irq, void *data)
 	struct snd_pcm_substream *substream;
 	u32 sistr;
 
-	spin_lock(&priv->lock);
-
-	sistr = msiof_read(priv, SISTR);
-	msiof_status_clear(priv);
-
-	spin_unlock(&priv->lock);
+	scoped_guard(spinlock, &priv->lock) {
+		sistr = msiof_read(priv, SISTR);
+		msiof_write(priv, SISTR, SISTR_ERR_TX | SISTR_ERR_RX);
+	}
 
 	/* overflow/underflow error */
 	substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK];
diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c
index f47bf38..6a3dbc8 100644
--- a/sound/soc/renesas/rcar/src.c
+++ b/sound/soc/renesas/rcar/src.c
@@ -558,19 +558,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	bool stop = false;
 
-	spin_lock(&priv->lock);
+	scoped_guard(spinlock, &priv->lock) {
+		/* ignore all cases if not working */
+		if (!rsnd_io_is_working(io))
+			break;
 
-	/* ignore all cases if not working */
-	if (!rsnd_io_is_working(io))
-		goto rsnd_src_interrupt_out;
+		if (rsnd_src_error_occurred(mod))
+			stop = true;
 
-	if (rsnd_src_error_occurred(mod))
-		stop = true;
-
-	rsnd_src_status_clear(mod);
-rsnd_src_interrupt_out:
-
-	spin_unlock(&priv->lock);
+		rsnd_src_status_clear(mod);
+	}
 
 	if (stop)
 		snd_pcm_stop_xrun(io->substream);
diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c
index d52056c..0420041 100644
--- a/sound/soc/renesas/rcar/ssi.c
+++ b/sound/soc/renesas/rcar/ssi.c
@@ -680,32 +680,31 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
 	bool elapsed = false;
 	bool stop = false;
 
-	spin_lock(&priv->lock);
+	scoped_guard(spinlock, &priv->lock) {
 
-	/* ignore all cases if not working */
-	if (!rsnd_io_is_working(io))
-		goto rsnd_ssi_interrupt_out;
+		/* ignore all cases if not working */
+		if (!rsnd_io_is_working(io))
+			break;
 
-	status = rsnd_ssi_status_get(mod);
+		status = rsnd_ssi_status_get(mod);
 
-	/* PIO only */
-	if (!is_dma && (status & DIRQ))
-		elapsed = rsnd_ssi_pio_interrupt(mod, io);
+		/* PIO only */
+		if (!is_dma && (status & DIRQ))
+			elapsed = rsnd_ssi_pio_interrupt(mod, io);
 
-	/* DMA only */
-	if (is_dma && (status & (UIRQ | OIRQ))) {
-		rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
-				      rsnd_mod_name(mod), status);
+		/* DMA only */
+		if (is_dma && (status & (UIRQ | OIRQ))) {
+			rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
+					      rsnd_mod_name(mod), status);
 
-		stop = true;
+			stop = true;
+		}
+
+		stop |= rsnd_ssiu_busif_err_status_clear(mod);
+
+		rsnd_ssi_status_clear(mod);
 	}
 
-	stop |= rsnd_ssiu_busif_err_status_clear(mod);
-
-	rsnd_ssi_status_clear(mod);
-rsnd_ssi_interrupt_out:
-	spin_unlock(&priv->lock);
-
 	if (elapsed)
 		snd_pcm_period_elapsed(io->substream);
 
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
index 0f7458a..e009408 100644
--- a/sound/soc/renesas/rz-ssi.c
+++ b/sound/soc/renesas/rz-ssi.c
@@ -188,24 +188,18 @@ static void rz_ssi_set_substream(struct rz_ssi_stream *strm,
 				 struct snd_pcm_substream *substream)
 {
 	struct rz_ssi_priv *ssi = strm->priv;
-	unsigned long flags;
 
-	spin_lock_irqsave(&ssi->lock, flags);
+	guard(spinlock_irqsave)(&ssi->lock);
+
 	strm->substream = substream;
-	spin_unlock_irqrestore(&ssi->lock, flags);
 }
 
 static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi,
 				   struct rz_ssi_stream *strm)
 {
-	unsigned long flags;
-	bool ret;
+	guard(spinlock_irqsave)(&ssi->lock);
 
-	spin_lock_irqsave(&ssi->lock, flags);
-	ret = strm->substream && strm->substream->runtime;
-	spin_unlock_irqrestore(&ssi->lock, flags);
-
-	return ret;
+	return strm->substream && strm->substream->runtime;
 }
 
 static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index cc9125f..9dd84d7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -717,7 +717,7 @@ int snd_soc_suspend(struct device *dev)
 				 * means it's doing something,
 				 * otherwise fall through.
 				 */
-				if (dapm->idle_bias_off) {
+				if (!dapm->idle_bias) {
 					dev_dbg(component->dev,
 						"ASoC: idle_bias_off CODEC on over suspend\n");
 					break;
@@ -1652,7 +1652,7 @@ static int soc_probe_component(struct snd_soc_card *card,
 	if (ret < 0)
 		goto err_probe;
 
-	WARN(dapm->idle_bias_off &&
+	WARN(!dapm->idle_bias &&
 	     dapm->bias_level != SND_SOC_BIAS_OFF,
 	     "codec %s can not start from non-off bias with idle_bias_off==1\n",
 	     component->name);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a37d44c..51fb09d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -165,6 +165,27 @@ static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
 	kfree(buf);
 }
 
+struct device *snd_soc_dapm_to_dev(struct snd_soc_dapm_context *dapm)
+{
+	if (dapm->component)
+		return dapm->component->dev;
+
+	return dapm->card->dev;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_to_dev);
+
+struct snd_soc_card *snd_soc_dapm_to_card(struct snd_soc_dapm_context *dapm)
+{
+	return dapm->card;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_to_card);
+
+struct snd_soc_component *snd_soc_dapm_to_component(struct snd_soc_dapm_context *dapm)
+{
+	return dapm->component;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_to_component);
+
 static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)
 {
 	return !list_empty(&w->dirty);
@@ -838,13 +859,13 @@ static struct list_head *dapm_kcontrol_get_path_list(
 	list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \
 		list_kcontrol)
 
-unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
+unsigned int snd_soc_dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
 {
 	struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
 
 	return data->value;
 }
-EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_get_value);
 
 static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
 	unsigned int value)
@@ -877,31 +898,42 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
 }
 
 /**
- * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a
+ * snd_soc_dapm_kcontrol_to_widget() - Returns the widget associated to a
  *   kcontrol
  * @kcontrol: The kcontrol
  */
-struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
-				struct snd_kcontrol *kcontrol)
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_to_widget(struct snd_kcontrol *kcontrol)
 {
 	return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_to_widget);
 
 /**
- * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
- *  kcontrol
+ * snd_soc_dapm_kcontrol_to_dapm() - Returns the dapm context associated to a kcontrol
  * @kcontrol: The kcontrol
  *
  * Note: This function must only be used on kcontrols that are known to have
  * been registered for a CODEC. Otherwise the behaviour is undefined.
  */
-struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
-	struct snd_kcontrol *kcontrol)
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_to_dapm(struct snd_kcontrol *kcontrol)
 {
 	return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_to_dapm);
+
+/**
+ * snd_soc_dapm_kcontrol_to_component() - Returns the component associated to a
+ * kcontrol
+ * @kcontrol: The kcontrol
+ *
+ * This function must only be used on DAPM contexts that are known to be part of
+ * a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined
+ */
+struct snd_soc_component *snd_soc_dapm_kcontrol_to_component(struct snd_kcontrol *kcontrol)
+{
+	return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_to_dapm(kcontrol));
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_to_component);
 
 static void dapm_reset(struct snd_soc_card *card)
 {
@@ -1000,6 +1032,25 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
 EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level);
 
 /**
+ * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level
+ * @dapm: The DAPM context to initialize
+ * @level: The DAPM level to initialize to
+ *
+ * This function only sets the driver internal state of the DAPM level and will
+ * not modify the state of the device. Hence it should not be used during normal
+ * operation, but only to synchronize the internal state to the device state.
+ * E.g. during driver probe to set the DAPM level to the one corresponding with
+ * the power-on reset state of the device.
+ *
+ * To change the DAPM state of the device use snd_soc_dapm_set_bias_level().
+ */
+void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+	dapm->bias_level = level;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_init_bias_level);
+
+/**
  * snd_soc_dapm_set_bias_level - set the bias level for the system
  * @dapm: DAPM context
  * @level: level to configure
@@ -1037,6 +1088,18 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
 	return ret;
 }
 
+/**
+ * snd_soc_dapm_get_bias_level() - Get current DAPM bias level
+ * @dapm: The context for which to get the bias level
+ *
+ * Returns: The current bias level of the passed DAPM context.
+ */
+enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm)
+{
+	return dapm->bias_level;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_bias_level);
+
 static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
 	struct snd_soc_dapm_widget *kcontrolw,
 	const struct snd_kcontrol_new *kcontrol_new,
@@ -2117,21 +2180,26 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
 		dapm_seq_insert(w, down_list, false);
 }
 
-static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm)
+bool snd_soc_dapm_get_idle_bias(struct snd_soc_dapm_context *dapm)
 {
-	if (dapm->idle_bias_off)
-		return true;
+	if (dapm->idle_bias) {
+		struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+		unsigned int state = snd_power_get_state(dapm->card->snd_card);
 
-	switch (snd_power_get_state(dapm->card->snd_card)) {
-	case SNDRV_CTL_POWER_D3hot:
-	case SNDRV_CTL_POWER_D3cold:
-		return dapm->suspend_bias_off;
-	default:
-		break;
+		if ((state == SNDRV_CTL_POWER_D3hot || (state == SNDRV_CTL_POWER_D3cold)) &&
+		    component)
+			return !component->driver->suspend_bias_off;
 	}
 
-	return false;
+	return dapm->idle_bias;
 }
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_idle_bias);
+
+void snd_soc_dapm_set_idle_bias(struct snd_soc_dapm_context *dapm, bool on)
+{
+	dapm->idle_bias = on;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_set_idle_bias);
 
 /*
  * Scan each dapm widget for complete audio path.
@@ -2158,10 +2226,10 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
 	trace_snd_soc_dapm_start(card, event);
 
 	for_each_card_dapms(card, d) {
-		if (dapm_idle_bias_off(d))
-			d->target_bias_level = SND_SOC_BIAS_OFF;
-		else
+		if (snd_soc_dapm_get_idle_bias(d))
 			d->target_bias_level = SND_SOC_BIAS_STANDBY;
+		else
+			d->target_bias_level = SND_SOC_BIAS_OFF;
 	}
 
 	dapm_reset(card);
@@ -2225,7 +2293,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event,
 		if (d->target_bias_level > bias)
 			bias = d->target_bias_level;
 	for_each_card_dapms(card, d)
-		if (!dapm_idle_bias_off(d))
+		if (snd_soc_dapm_get_idle_bias(d))
 			d->target_bias_level = bias;
 
 	trace_snd_soc_dapm_walk_done(card);
@@ -4759,8 +4827,7 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
 
 	if (component) {
 		dapm->dev		= component->dev;
-		dapm->idle_bias_off	= !component->driver->idle_bias_on;
-		dapm->suspend_bias_off	= component->driver->suspend_bias_off;
+		dapm->idle_bias		= component->driver->idle_bias_on;
 	} else {
 		dapm->dev		= card->dev;
 	}
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index f00b381..40debc5 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -354,8 +354,8 @@ static int imx_probe(struct snd_sof_dev *sdev)
 
 	common = devm_kzalloc(sdev->dev, sizeof(*common), GFP_KERNEL);
 	if (!common)
-		return dev_err_probe(sdev->dev, -ENOMEM,
-				     "failed to allocate common data\n");
+		return -ENOMEM;
+
 	sdev->pdata->hw_pdata = common;
 
 	common->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
@@ -382,7 +382,7 @@ static int imx_probe(struct snd_sof_dev *sdev)
 				       imx_unregister_action,
 				       sdev);
 	if (ret)
-		return dev_err_probe(sdev->dev, ret, "failed to add devm action\n");
+		return ret;
 
 	common->ipc_handle = dev_get_drvdata(&common->ipc_dev->dev);
 	if (!common->ipc_handle)
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index b73dd91..7e9eab2 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -171,8 +171,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
 
 	chip = devm_kzalloc(sdev->dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
-		return dev_err_probe(sdev->dev, -ENOMEM,
-				     "failed to allocate chip data\n");
+		return -ENOMEM;
 
 	chip->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
 	if (!chip->dap)
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 2f99258..37674ea 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -260,9 +260,6 @@ void hda_codec_detect_mask(struct snd_sof_dev *sdev)
 	    sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
 		return;
 
-	/* Accept unsolicited responses */
-	snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
-
 	/* detect codecs */
 	if (!bus->codec_mask) {
 		bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 4f34fd9..8332d4b 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -183,7 +183,7 @@ int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
 }
 EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
-int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev)
+int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec)
 {
 	struct hdac_bus *bus = sof_to_bus(sdev);
 	struct hdac_stream *stream;
@@ -220,7 +220,11 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev)
 	}
 	usleep_range(1000, 1200);
 
-	hda_codec_detect_mask(sdev);
+	/* Accept unsolicited responses */
+	snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+
+	if (detect_codec)
+		hda_codec_detect_mask(sdev);
 
 	/* clear stream status */
 	list_for_each_entry(stream, &bus->stream_list, list) {
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index f64e8a6..3ab6d5c 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -870,7 +870,7 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
 	snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0);
 
 	/* reset and start hda controller */
-	ret = hda_dsp_ctrl_init_chip(sdev);
+	ret = hda_dsp_ctrl_init_chip(sdev, false);
 	if (ret < 0) {
 		dev_err(sdev->dev,
 			"error: failed to start controller after resume\n");
diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c
index 1327f1c..ff5abccf 100644
--- a/sound/soc/sof/intel/hda-sdw-bpt.c
+++ b/sound/soc/sof/intel/hda-sdw-bpt.c
@@ -150,7 +150,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream
 	u32 mask;
 	int ret;
 
-	ret = hda_cl_cleanup(sdev->dev, dmab_bdl, true, sdw_bpt_stream);
+	ret = hda_cl_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream);
 	if (ret < 0) {
 		dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n",
 			__func__);
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index c387efe..52e86fa 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -616,7 +616,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
 		dev_dbg(sdev->dev, "PP capability, will probe DSP later.\n");
 
 	/* Init HDA controller after i915 init */
-	ret = hda_dsp_ctrl_init_chip(sdev);
+	ret = hda_dsp_ctrl_init_chip(sdev, true);
 	if (ret < 0) {
 		dev_err(bus->dev, "error: init chip failed with ret: %d\n",
 			ret);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index e14f82c..28daf0a 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -757,7 +757,7 @@ void hda_dsp_ctrl_ppcap_int_enable(struct snd_sof_dev *sdev, bool enable);
 int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev, bool reset);
 void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable);
 int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable);
-int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev);
+int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec);
 void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev);
 /*
  * HDA bus operations.
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index e5c8fec..6ec391f 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -126,7 +126,7 @@ static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
 		capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
 		entry = strchr(entry + 1, entry_delimiter[0]);
 	}
-	*out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
+	*out = kmalloc_array(capacity, sizeof(**out), GFP_KERNEL);
 	if (!*out)
 		return -ENOMEM;
 
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 374dc10..cb9a067 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -727,6 +727,58 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev,
 	return 0;
 }
 
+static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev,
+						struct snd_pcm_hw_params *params,
+						struct sof_ipc4_copier *ipc4_copier)
+{
+	struct sof_ipc4_pin_format *pin_fmts = ipc4_copier->available_fmt.input_pin_fmts;
+	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	int num_input_formats = ipc4_copier->available_fmt.num_input_formats;
+	unsigned int fe_channels = params_channels(params);
+	bool fe_be_match = false;
+	bool single_be_channels = true;
+	unsigned int be_channels, val;
+	int i;
+
+	if (WARN_ON_ONCE(!num_input_formats))
+		return -EINVAL;
+
+	/*
+	 * Copier does not change channels, so we
+	 * need to only consider the input pin information.
+	 */
+	be_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(pin_fmts[0].audio_fmt.fmt_cfg);
+	for (i = 0; i < num_input_formats; i++) {
+		val = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(pin_fmts[i].audio_fmt.fmt_cfg);
+
+		if (val != be_channels)
+			single_be_channels = false;
+
+		if (val == fe_channels) {
+			fe_be_match = true;
+			break;
+		}
+	}
+
+	/*
+	 * If channels is different than FE channels, topology must contain a
+	 * module which can change the number of channels. But we do require
+	 * topology to define a single channels in the DAI copier config in
+	 * this case (FE channels may be variable).
+	 */
+	if (!fe_be_match) {
+		if (!single_be_channels) {
+			dev_err(sdev->dev, "Unable to select channels for DAI link\n");
+			return -EINVAL;
+		}
+
+		channels->min = be_channels;
+		channels->max = be_channels;
+	}
+
+	return 0;
+}
+
 static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
 				       struct snd_pcm_hw_params *params)
 {
@@ -790,6 +842,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
 	if (ret)
 		return ret;
 
+	ret = sof_ipc4_pcm_dai_link_fixup_channels(sdev, params, ipc4_copier);
+	if (ret)
+		return ret;
+
 	if (single_bitdepth) {
 		snd_mask_none(fmt);
 		valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(ipc4_fmt->fmt_cfg);
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 591ee30..b6a732d 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -38,6 +38,36 @@ MODULE_PARM_DESC(ipc4_ignore_cpc,
 static DEFINE_IDA(alh_group_ida);
 static DEFINE_IDA(pipeline_ida);
 
+struct sof_comp_domains {
+	const char *name;
+	enum sof_comp_domain domain;
+};
+
+static const struct sof_comp_domains sof_domains[] = {
+	{ "LL", SOF_COMP_DOMAIN_LL, },
+	{ "DP", SOF_COMP_DOMAIN_DP, }
+};
+
+static enum sof_comp_domain find_domain(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(sof_domains); i++) {
+		if (strcmp(name, sof_domains[i].name) == 0)
+			return sof_domains[i].domain;
+	}
+	/* No valid value found, fall back to manifest value */
+	return SOF_COMP_DOMAIN_UNSET;
+}
+
+static int get_token_comp_domain(void *elem, void *object, u32 offset)
+{
+	u32 *val = (u32 *)((u8 *)object + offset);
+
+	*val = find_domain((const char *)elem);
+	return 0;
+}
+
 static const struct sof_topology_token ipc4_sched_tokens[] = {
 	{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
 		offsetof(struct sof_ipc4_pipeline, lp_mode)},
@@ -127,6 +157,8 @@ static const struct sof_topology_token comp_ext_tokens[] = {
 		offsetof(struct snd_sof_widget, uuid)},
 	{SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
 		offsetof(struct snd_sof_widget, core)},
+	{SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain,
+		offsetof(struct snd_sof_widget, comp_domain)},
 };
 
 static const struct sof_topology_token gain_tokens[] = {
@@ -497,7 +529,17 @@ static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_
 
 	msg->extension = SOF_IPC4_MOD_EXT_CORE_ID(swidget->core);
 
-	type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;
+	switch (swidget->comp_domain) {
+	case SOF_COMP_DOMAIN_LL:
+		type = 0;
+		break;
+	case SOF_COMP_DOMAIN_DP:
+		type = 1;
+		break;
+	default:
+		type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;
+		break;
+	}
 	msg->extension |= SOF_IPC4_MOD_EXT_DOMAIN(type);
 
 	return 0;
@@ -1292,6 +1334,23 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
 	return 0;
 }
 
+static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg)
+{
+	/* Fetch  the sample type from the fmt for 8 and 32 bit formats */
+	u32 __bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt_cfg);
+
+	if (__bits == 8 || __bits == 32)
+		return SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(fmt_cfg);
+
+	/*
+	 * Return LSB integer type for 20 and 24 formats as the firmware is
+	 * handling the LSB/MSB alignment internally, for the kernel this
+	 * should not be taken into account, we treat them as LSB to match with
+	 * the format we support on the PCM side.
+	 */
+	return SOF_IPC4_TYPE_LSB_INTEGER;
+}
+
 /* update hw_params based on the audio stream format */
 static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
 				     struct sof_ipc4_audio_format *fmt, u32 param_to_update)
@@ -1300,10 +1359,27 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
 
 	if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {
 		int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+		int type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg);
 		snd_pcm_format_t snd_fmt;
 		struct snd_mask *m;
 
 		switch (valid_bits) {
+		case 8:
+			switch (type) {
+			case SOF_IPC4_TYPE_A_LAW:
+				snd_fmt = SNDRV_PCM_FORMAT_A_LAW;
+				break;
+			case SOF_IPC4_TYPE_MU_LAW:
+				snd_fmt = SNDRV_PCM_FORMAT_MU_LAW;
+				break;
+			case SOF_IPC4_TYPE_UNSIGNED_INTEGER:
+				snd_fmt = SNDRV_PCM_FORMAT_U8;
+				break;
+			default:
+				dev_err(sdev->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type);
+				return -EINVAL;
+			}
+			break;
 		case 16:
 			snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
 			break;
@@ -1311,7 +1387,17 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
 			snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
 			break;
 		case 32:
-			snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+			switch (type) {
+			case SOF_IPC4_TYPE_LSB_INTEGER:
+				snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+				break;
+			case SOF_IPC4_TYPE_FLOAT:
+				snd_fmt = SNDRV_PCM_FORMAT_FLOAT_LE;
+				break;
+			default:
+				dev_err(sdev->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type);
+				return -EINVAL;
+			}
 			break;
 		default:
 			dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
@@ -1375,7 +1461,7 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
 					  struct sof_ipc4_base_module_cfg *base_config,
 					  struct sof_ipc4_available_audio_format *available_fmt,
 					  u32 out_ref_rate, u32 out_ref_channels,
-					  u32 out_ref_valid_bits)
+					  u32 out_ref_valid_bits, u32 out_ref_type)
 {
 	struct sof_ipc4_pin_format *pin_fmts = available_fmt->output_pin_fmts;
 	u32 pin_fmts_size = available_fmt->num_output_formats;
@@ -1401,19 +1487,22 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
 	for (i = 0; i < pin_fmts_size; i++) {
 		struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
 
-		u32 _out_rate, _out_channels, _out_valid_bits;
+		u32 _out_rate, _out_channels, _out_valid_bits, _out_type;
 
 		_out_rate = fmt->sampling_frequency;
 		_out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
 		_out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+		_out_type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg);
 
 		if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&
-		    _out_valid_bits == out_ref_valid_bits)
+		    _out_valid_bits == out_ref_valid_bits && _out_type == out_ref_type)
 			goto out_fmt;
 	}
 
-	dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
-		__func__, out_ref_rate, out_ref_valid_bits, out_ref_channels);
+	dev_err(sdev->dev,
+		"%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n",
+		__func__, out_ref_rate, out_ref_valid_bits, out_ref_channels,
+		out_ref_type);
 
 	return -EINVAL;
 
@@ -1426,18 +1515,46 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
 static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
 {
 	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_MU_LAW:
+	case SNDRV_PCM_FORMAT_A_LAW:
+		return 8;
 	case SNDRV_PCM_FORMAT_S16_LE:
 		return 16;
 	case SNDRV_PCM_FORMAT_S24_LE:
 		return 24;
 	case SNDRV_PCM_FORMAT_S32_LE:
 		return 32;
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+		return 32;
 	default:
 		dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));
 		return -EINVAL;
 	}
 }
 
+static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
+{
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_A_LAW:
+		return SOF_IPC4_TYPE_A_LAW;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		return SOF_IPC4_TYPE_MU_LAW;
+	case SNDRV_PCM_FORMAT_U8:
+		return SOF_IPC4_TYPE_UNSIGNED_INTEGER;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		return SOF_IPC4_TYPE_LSB_INTEGER;
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+		return SOF_IPC4_TYPE_FLOAT;
+	default:
+		dev_err(sdev->dev, "invalid pcm sample type %d\n", params_format(params));
+		return -EINVAL;
+	}
+}
+
 static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
 					 struct snd_sof_widget *swidget,
 					 struct sof_ipc4_base_module_cfg *base_config,
@@ -1449,8 +1566,10 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
 	u32 valid_bits;
 	u32 channels;
 	u32 rate;
+	u32 type;
 	bool single_format;
 	int sample_valid_bits;
+	int sample_type;
 	int i = 0;
 
 	if (!pin_fmts_size) {
@@ -1466,6 +1585,10 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
 	if (sample_valid_bits < 0)
 		return sample_valid_bits;
 
+	sample_type = sof_ipc4_get_sample_type(sdev, params);
+	if (sample_type < 0)
+		return sample_type;
+
 	/*
 	 * Search supported input audio formats with pin index 0 to match rate, channels and
 	 * sample_valid_bits from reference params
@@ -1479,14 +1602,17 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
 		rate = fmt->sampling_frequency;
 		channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
 		valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+		type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg);
 		if (params_rate(params) == rate && params_channels(params) == channels &&
-		    sample_valid_bits == valid_bits)
+		    sample_valid_bits == valid_bits && sample_type == type)
 			break;
 	}
 
 	if (i == pin_fmts_size) {
-		dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
-			__func__, params_rate(params), sample_valid_bits, params_channels(params));
+		dev_err(sdev->dev,
+			"%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n",
+			__func__, params_rate(params), sample_valid_bits,
+			params_channels(params), sample_type);
 		return -EINVAL;
 	}
 
@@ -1882,7 +2008,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
 	int *ipc_config_size;
 	u32 **data;
 	int ipc_size, ret, out_ref_valid_bits;
-	u32 out_ref_rate, out_ref_channels;
+	u32 out_ref_rate, out_ref_channels, out_ref_type;
 	u32 deep_buffer_dma_ms = 0;
 	bool single_output_bitdepth;
 	int i;
@@ -1923,10 +2049,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
 			host_dma_id = platform_params->stream_tag - 1;
 			pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
 
-			/* Set SCS bit for S16_LE format only */
 			if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)
 				pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
 
+			/* Set SCS bit for 8 and 16 bit formats */
+			if (params_physical_width(fe_params) <= 16)
+				pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
+
 			/*
 			 * Despite its name the bitfield 'fifo_size' is used to define DMA buffer
 			 * size. The expression calculates 2ms buffer size.
@@ -2051,6 +2180,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
 		in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
 		out_ref_rate = in_fmt->sampling_frequency;
 		out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+		out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
 
 		if (!single_output_bitdepth)
 			out_ref_valid_bits =
@@ -2061,6 +2191,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
 	case snd_soc_dapm_dai_in:
 		out_ref_rate = params_rate(fe_params);
 		out_ref_channels = params_channels(fe_params);
+		ret = sof_ipc4_get_sample_type(sdev, fe_params);
+		if (ret < 0)
+			return ret;
+		out_ref_type = (u32)ret;
+
 		if (!single_output_bitdepth) {
 			out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
 			if (out_ref_valid_bits < 0)
@@ -2085,12 +2220,14 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
 		out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
 		out_ref_valid_bits =
 			SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
+		out_ref_type = sof_ipc4_fmt_cfg_to_type(out_fmt->fmt_cfg);
 	}
 
 	output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
 							  &copier_data->base_config,
 							  available_fmt, out_ref_rate,
-							  out_ref_channels, out_ref_valid_bits);
+							  out_ref_channels, out_ref_valid_bits,
+							  out_ref_type);
 	if (output_fmt_index < 0)
 		return output_fmt_index;
 
@@ -2319,7 +2456,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
 	struct sof_ipc4_gain *gain = swidget->private;
 	struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
 	struct sof_ipc4_audio_format *in_fmt;
-	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
+	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type;
 	int input_fmt_index, output_fmt_index;
 
 	input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
@@ -2333,13 +2470,15 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
 	out_ref_rate = in_fmt->sampling_frequency;
 	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
 	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+	out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
 
 	output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
 							  &gain->data.base_config,
 							  available_fmt,
 							  out_ref_rate,
 							  out_ref_channels,
-							  out_ref_valid_bits);
+							  out_ref_valid_bits,
+							  out_ref_type);
 	if (output_fmt_index < 0)
 		return output_fmt_index;
 
@@ -2362,7 +2501,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
 	struct sof_ipc4_mixer *mixer = swidget->private;
 	struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
 	struct sof_ipc4_audio_format *in_fmt;
-	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
+	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type;
 	int input_fmt_index, output_fmt_index;
 
 	input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
@@ -2376,13 +2515,15 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
 	out_ref_rate = in_fmt->sampling_frequency;
 	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
 	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+	out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
 
 	output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
 							  &mixer->base_config,
 							  available_fmt,
 							  out_ref_rate,
 							  out_ref_channels,
-							  out_ref_valid_bits);
+							  out_ref_valid_bits,
+							  out_ref_type);
 	if (output_fmt_index < 0)
 		return output_fmt_index;
 
@@ -2406,7 +2547,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
 	struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
 	struct sof_ipc4_audio_format *out_audio_fmt;
 	struct sof_ipc4_audio_format *in_audio_fmt;
-	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
+	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type;
 	int output_fmt_index, input_fmt_index;
 
 	input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
@@ -2433,6 +2574,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
 	in_audio_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
 	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg);
 	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg);
+	out_ref_type = sof_ipc4_fmt_cfg_to_type(in_audio_fmt->fmt_cfg);
 
 	/*
 	 * For capture, the SRC module should convert the rate to match the rate requested by the
@@ -2446,7 +2588,8 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
 							  available_fmt,
 							  out_ref_rate,
 							  out_ref_channels,
-							  out_ref_valid_bits);
+							  out_ref_valid_bits,
+							  out_ref_type);
 	if (output_fmt_index < 0)
 		return output_fmt_index;
 
@@ -2570,20 +2713,22 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
 		struct sof_ipc4_audio_format *in_fmt;
 		struct sof_ipc4_pin_format *pin_fmt;
 		u32 out_ref_rate, out_ref_channels;
-		int out_ref_valid_bits;
+		int out_ref_valid_bits, out_ref_type;
 
 		in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
 
 		out_ref_rate = in_fmt->sampling_frequency;
 		out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
 		out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+		out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
 
 		output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
 								  &process->base_config,
 								  available_fmt,
 								  out_ref_rate,
 								  out_ref_channels,
-								  out_ref_valid_bits);
+								  out_ref_valid_bits,
+								  out_ref_type);
 		if (output_fmt_index < 0)
 			return output_fmt_index;
 
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 14ba58d..dfa1a6c 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -41,6 +41,15 @@
 #define SOF_IPC4_FW_MAX_PAGE_COUNT 20
 #define SOF_IPC4_FW_MAX_QUEUE_COUNT 8
 
+/* IPC4 sample types */
+#define SOF_IPC4_TYPE_MSB_INTEGER 0
+#define SOF_IPC4_TYPE_LSB_INTEGER 1
+#define SOF_IPC4_TYPE_SIGNED_INTEGER 2
+#define SOF_IPC4_TYPE_UNSIGNED_INTEGER 3
+#define SOF_IPC4_TYPE_FLOAT 4
+#define SOF_IPC4_TYPE_A_LAW 5
+#define SOF_IPC4_TYPE_MU_LAW 6
+
 /* Node index and mask applicable for host copier and ALH/HDA type DAI copiers */
 #define SOF_IPC4_NODE_INDEX_MASK	0xFF
 #define SOF_IPC4_NODE_INDEX(x)	((x) & SOF_IPC4_NODE_INDEX_MASK)
@@ -109,6 +118,13 @@ enum sof_ipc4_copier_module_config_params {
 	SOF_IPC4_COPIER_MODULE_CFG_ATTENUATION,
 };
 
+/* Scheduling domain, unset, Low Latency, or Data Processing */
+enum sof_comp_domain {
+	SOF_COMP_DOMAIN_UNSET = 0,	/* Take domain value from manifest */
+	SOF_COMP_DOMAIN_LL,		/* Low Latency scheduling domain */
+	SOF_COMP_DOMAIN_DP,		/* Data Processing scheduling domain */
+};
+
 struct sof_ipc4_copier_config_set_sink_format {
 /* Id of sink */
 	u32 sink_id;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 36ab75e1..db6973c 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -451,6 +451,9 @@ struct snd_sof_widget {
 	 */
 	bool dynamic_pipeline_widget;
 
+	/* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */
+	u32 comp_domain;
+
 	struct snd_soc_dapm_widget *widget;
 	struct list_head list;	/* list in sdev widget list */
 	struct snd_sof_pipeline *spipe;
diff --git a/sound/soc/sof/sof-client-probes-ipc3.c b/sound/soc/sof/sof-client-probes-ipc3.c
index 816df74..a78ec09 100644
--- a/sound/soc/sof/sof-client-probes-ipc3.c
+++ b/sound/soc/sof/sof-client-probes-ipc3.c
@@ -100,9 +100,11 @@ static int ipc3_probes_deinit(struct sof_client_dev *cdev)
 }
 
 static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
-			    void **params, size_t *num_params)
+			    void **params, size_t *num_params,
+			    enum sof_probe_info_type type)
 {
 	size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
+	struct device *dev = &cdev->auxdev.dev;
 	struct sof_ipc_probe_info_params msg = {{{0}}};
 	struct sof_ipc_probe_info_params *reply;
 	size_t bytes;
@@ -111,6 +113,11 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
 	*params = NULL;
 	*num_params = 0;
 
+	if (type != PROBES_INFO_ACTIVE_PROBES) {
+		dev_err(dev, "%s: info type %u not supported", __func__, type);
+		return -EOPNOTSUPP;
+	}
+
 	reply = kzalloc(max_msg_size, GFP_KERNEL);
 	if (!reply)
 		return -ENOMEM;
@@ -142,21 +149,25 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
 }
 
 /**
- * ipc3_probes_points_info - retrieve list of active probe points
+ * ipc3_probes_points_info - retrieve list of probe points
  * @cdev:		SOF client device
  * @desc:	Returned list of active probes
  * @num_desc:	Returned count of active probes
+ * @type:	Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
  *
- * Host sends PROBE_POINT_INFO request to obtain list of active probe
- * points, valid for disconnection when given probe is no longer
- * required.
+ * If type is PROBES_INFO_ACTIVE_PROBES, host sends PROBE_POINT_INFO
+ * request to obtain list of active probe points, valid for
+ * disconnection when given probe is no longer required.
+ *
+ * Type PROBES_INFO_AVAILABE_PROBES is not yet supported.
  */
 static int ipc3_probes_points_info(struct sof_client_dev *cdev,
 				   struct sof_probe_point_desc **desc,
-				   size_t *num_desc)
+				   size_t *num_desc,
+				   enum sof_probe_info_type type)
 {
 	return ipc3_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
-			       (void **)desc, num_desc);
+				(void **)desc, num_desc, type);
 }
 
 /**
diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c
index 603aed222..758a56d 100644
--- a/sound/soc/sof/sof-client-probes-ipc4.c
+++ b/sound/soc/sof/sof-client-probes-ipc4.c
@@ -8,7 +8,7 @@
 #include <sound/soc.h>
 #include <sound/sof/ipc4/header.h>
 #include <uapi/sound/sof/header.h>
-#include "sof-priv.h"
+#include "sof-audio.h"
 #include "ipc4-priv.h"
 #include "sof-client.h"
 #include "sof-client-probes.h"
@@ -28,6 +28,7 @@ enum sof_ipc4_probe_runtime_param {
 	SOF_IPC4_PROBE_INJECTION_DMA_DETACH,
 	SOF_IPC4_PROBE_POINTS,
 	SOF_IPC4_PROBE_POINTS_DISCONNECT,
+	SOF_IPC4_PROBE_POINTS_AVAILABLE,
 };
 
 struct sof_ipc4_probe_gtw_cfg {
@@ -49,14 +50,42 @@ enum sof_ipc4_probe_type {
 	SOF_IPC4_PROBE_TYPE_INTERNAL
 };
 
+#define SOF_IPC4_PROBE_TYPE_SHIFT		24
+#define SOF_IPC4_PROBE_TYPE_MASK		GENMASK(25, 24)
+#define SOF_IPC4_PROBE_TYPE_GET(x)		(((x) & SOF_IPC4_PROBE_TYPE_MASK) \
+						 >> SOF_IPC4_PROBE_TYPE_SHIFT)
+#define SOF_IPC4_PROBE_IDX_SHIFT		26
+#define SOF_IPC4_PROBE_IDX_MASK			GENMASK(31, 26)
+#define SOF_IPC4_PROBE_IDX_GET(x)		(((x) & SOF_IPC4_PROBE_IDX_MASK) \
+						 >> SOF_IPC4_PROBE_IDX_SHIFT)
+
 struct sof_ipc4_probe_point {
 	u32 point_id;
 	u32 purpose;
 	u32 stream_tag;
 } __packed __aligned(4);
 
+struct sof_ipc4_probe_info {
+	unsigned int num_elems;
+	DECLARE_FLEX_ARRAY(struct sof_ipc4_probe_point, points);
+} __packed;
+
 #define INVALID_PIPELINE_ID      0xFF
 
+static const char *sof_probe_ipc4_type_string(u32 type)
+{
+	switch (type) {
+	case SOF_IPC4_PROBE_TYPE_INPUT:
+		return "input";
+	case SOF_IPC4_PROBE_TYPE_OUTPUT:
+		return "output";
+	case SOF_IPC4_PROBE_TYPE_INTERNAL:
+		return "internal";
+	default:
+		return "UNKNOWN";
+	}
+}
+
 /**
  * sof_ipc4_probe_get_module_info - Get IPC4 module info for probe module
  * @cdev:		SOF client device
@@ -164,25 +193,113 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev)
 }
 
 /**
- * ipc4_probes_points_info - retrieve list of active probe points
+ * ipc4_probes_points_info - retrieve list of probe points
  * @cdev:	SOF client device
  * @desc:	Returned list of active probes
  * @num_desc:	Returned count of active probes
+ * @type:	Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
  * @return:	0 on success, negative error code on error
  *
- * Dummy implementation returning empty list of probes.
+ * Returns list if active probe points if type is
+ * PROBES_INFO_ACTIVE_PROBES, or list of all available probe points if
+ * type is PROBES_INFO_AVAILABE_PROBES.
  */
 static int ipc4_probes_points_info(struct sof_client_dev *cdev,
 				   struct sof_probe_point_desc **desc,
-				   size_t *num_desc)
+				   size_t *num_desc,
+				   enum sof_probe_info_type type)
 {
-	/* TODO: Firmware side implementation needed first */
-	*desc = NULL;
-	*num_desc = 0;
+	struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
+	struct device *dev = &cdev->auxdev.dev;
+	struct sof_ipc4_probe_info *info;
+	struct sof_ipc4_msg msg;
+	u32 param_id;
+	int i, ret;
+
+	if (!mentry)
+		return -ENODEV;
+
+	switch (type) {
+	case PROBES_INFO_ACTIVE_PROBES:
+		param_id = SOF_IPC4_PROBE_POINTS;
+		break;
+	case PROBES_INFO_AVAILABE_PROBES:
+		param_id = SOF_IPC4_PROBE_POINTS_AVAILABLE;
+		break;
+	default:
+		dev_err(dev, "%s: info type %u not supported", __func__, type);
+		return -EOPNOTSUPP;
+	}
+
+	msg.primary = mentry->id;
+	msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+	msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+
+	msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(param_id);
+
+	msg.data_size = sof_client_get_ipc_max_payload_size(cdev);
+	msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL);
+	if (!msg.data_ptr)
+		return -ENOMEM;
+
+	ret = sof_client_ipc_set_get_data(cdev, &msg, false);
+	if (ret) {
+		kfree(msg.data_ptr);
+		return ret;
+	}
+	info = msg.data_ptr;
+	*num_desc = info->num_elems;
+	dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc);
+
+	*desc = kzalloc(*num_desc * sizeof(**desc), GFP_KERNEL);
+	if (!*desc) {
+		kfree(msg.data_ptr);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < *num_desc; i++) {
+		(*desc)[i].buffer_id = info->points[i].point_id;
+		(*desc)[i].purpose = info->points[i].purpose;
+		(*desc)[i].stream_tag = info->points[i].stream_tag;
+	}
+	kfree(msg.data_ptr);
+
 	return 0;
 }
 
 /**
+ * ipc4_probes_point_print - Human readable print of probe point descriptor
+ * @cdev:	SOF client device
+ * @buf:	Buffer to print to
+ * @size:	Available bytes in buffer
+ * @desc:	Describes the probe point to print
+ * @return:	Number of bytes printed or an error code (snprintf return value)
+ */
+static int ipc4_probes_point_print(struct sof_client_dev *cdev, char *buf, size_t size,
+				   struct sof_probe_point_desc *desc)
+{
+	struct device *dev = &cdev->auxdev.dev;
+	struct snd_sof_widget *swidget;
+	int ret;
+
+	swidget = sof_client_ipc4_find_swidget_by_id(cdev, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
+						     SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
+	if (!swidget)
+		dev_err(dev, "%s: Failed to find widget for module %lu.%lu\n",
+			__func__, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
+			SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
+
+	ret = snprintf(buf, size, "%#x,%#x,%#x\t%s %s buf idx %lu %s\n",
+		       desc->buffer_id, desc->purpose, desc->stream_tag,
+		       swidget ? swidget->widget->name : "<unknown>",
+		       sof_probe_ipc4_type_string(SOF_IPC4_PROBE_TYPE_GET(desc->buffer_id)),
+		       SOF_IPC4_PROBE_IDX_GET(desc->buffer_id),
+		       desc->stream_tag ? "(connected)" : "");
+
+	return ret;
+}
+
+/**
  * ipc4_probes_points_add - connect specified probes
  * @cdev:	SOF client device
  * @desc:	List of probe points to connect
@@ -202,7 +319,7 @@ static int ipc4_probes_points_add(struct sof_client_dev *cdev,
 	int i, ret;
 
 	if (!mentry)
-		return -ENODEV;
+		return -EOPNOTSUPP;
 
 	/* The sof_probe_point_desc and sof_ipc4_probe_point structs
 	 * are of same size and even the integers are the same in the
@@ -286,6 +403,7 @@ const struct sof_probes_ipc_ops ipc4_probe_ops =  {
 	.init = ipc4_probes_init,
 	.deinit = ipc4_probes_deinit,
 	.points_info = ipc4_probes_points_info,
+	.point_print = ipc4_probes_point_print,
 	.points_add = ipc4_probes_points_add,
 	.points_remove = ipc4_probes_points_remove,
 };
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index 663c0d3c..aaf0ae4 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -17,8 +17,14 @@
 
 #include <sound/soc.h>
 #include <sound/sof/header.h>
+#include <sound/sof/ipc4/header.h>
 #include "sof-client.h"
 #include "sof-client-probes.h"
+#include "sof-audio.h"
+
+#ifdef CONFIG_SND_SOC_SOF_IPC4
+#include "ipc4-priv.h"
+#endif
 
 #define SOF_PROBES_SUSPEND_DELAY_MS 3000
 /* only extraction supported for now */
@@ -69,7 +75,8 @@ static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
 	int i, ret;
 
 	/* disconnect all probe points */
-	ret = ipc->points_info(cdev, &desc, &num_desc);
+	ret = ipc->points_info(cdev, &desc, &num_desc,
+			       PROBES_INFO_ACTIVE_PROBES);
 	if (ret < 0) {
 		dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
 		goto exit;
@@ -189,7 +196,8 @@ static const struct snd_compress_ops sof_probes_compressed_ops = {
 };
 
 static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
-					  size_t count, loff_t *ppos)
+					  size_t count, loff_t *ppos,
+					  enum sof_probe_info_type type)
 {
 	struct sof_client_dev *cdev = file->private_data;
 	struct sof_probes_priv *priv = cdev->data;
@@ -216,16 +224,20 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
 		goto exit;
 	}
 
-	ret = ipc->points_info(cdev, &desc, &num_desc);
+	ret = ipc->points_info(cdev, &desc, &num_desc, type);
 	if (ret < 0)
 		goto pm_error;
 
 	for (i = 0; i < num_desc; i++) {
 		offset = strlen(buf);
 		remaining = PAGE_SIZE - offset;
-		ret = snprintf(buf + offset, remaining,
-			       "Id: %#010x  Purpose: %u  Node id: %#x\n",
-				desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
+		if (ipc->point_print)
+			ret = ipc->point_print(cdev, buf + offset, remaining, &desc[i]);
+		else
+			ret = snprintf(buf + offset, remaining,
+				       "Id: %#010x  Purpose: %u  Node id: %#x\n",
+				       desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
+
 		if (ret < 0 || ret >= remaining) {
 			/* truncate the output buffer at the last full line */
 			buf[offset] = '\0';
@@ -247,6 +259,22 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
 	return ret;
 }
 
+static ssize_t sof_probes_dfs_active_points_read(struct file *file,
+						 char __user *to,
+						 size_t count, loff_t *ppos)
+{
+	return sof_probes_dfs_points_read(file, to, count, ppos,
+					  PROBES_INFO_ACTIVE_PROBES);
+}
+
+static ssize_t sof_probes_dfs_available_points_read(struct file *file,
+						    char __user *to,
+						    size_t count, loff_t *ppos)
+{
+	return sof_probes_dfs_points_read(file, to, count, ppos,
+					  PROBES_INFO_AVAILABE_PROBES);
+}
+
 static ssize_t
 sof_probes_dfs_points_write(struct file *file, const char __user *from,
 			    size_t count, loff_t *ppos)
@@ -296,15 +324,23 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
 	return ret;
 }
 
-static const struct file_operations sof_probes_points_fops = {
+static const struct file_operations sof_probes_active_points_fops = {
 	.open = simple_open,
-	.read = sof_probes_dfs_points_read,
+	.read = sof_probes_dfs_active_points_read,
 	.write = sof_probes_dfs_points_write,
 	.llseek = default_llseek,
 
 	.owner = THIS_MODULE,
 };
 
+static const struct file_operations sof_probes_available_points_fops = {
+	.open = simple_open,
+	.read = sof_probes_dfs_available_points_read,
+	.llseek = default_llseek,
+
+	.owner = THIS_MODULE,
+};
+
 static ssize_t
 sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
 				   size_t count, loff_t *ppos)
@@ -449,13 +485,17 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev,
 
 	/* create read-write probes_points debugfs entry */
 	priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
-					       cdev, &sof_probes_points_fops);
+					       cdev, &sof_probes_active_points_fops);
 
 	/* create read-write probe_points_remove debugfs entry */
 	priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
 						      dfsroot, cdev,
 						      &sof_probes_points_remove_fops);
 
+	/* create read-write probes_points debugfs entry */
+	priv->dfs_points = debugfs_create_file("probe_points_available", 0644, dfsroot,
+					       cdev, &sof_probes_available_points_fops);
+
 	links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
 	cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
 	if (!links || !cpus) {
@@ -485,7 +525,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev,
 	card->dai_link = links;
 
 	/* set idle_bias_off to prevent the core from resuming the card->dev */
-	card->dapm.idle_bias_off = true;
+	card->dapm.idle_bias = false;
 
 	snd_soc_card_set_drvdata(card, cdev);
 
diff --git a/sound/soc/sof/sof-client-probes.h b/sound/soc/sof/sof-client-probes.h
index da04d65..5fb9555 100644
--- a/sound/soc/sof/sof-client-probes.h
+++ b/sound/soc/sof/sof-client-probes.h
@@ -34,13 +34,20 @@ struct sof_probe_point_desc {
 	unsigned int stream_tag;
 } __packed;
 
+enum sof_probe_info_type {
+	PROBES_INFO_ACTIVE_PROBES,
+	PROBES_INFO_AVAILABE_PROBES,
+};
+
 struct sof_probes_ipc_ops {
 	int (*init)(struct sof_client_dev *cdev, u32 stream_tag,
 		    size_t buffer_size);
 	int (*deinit)(struct sof_client_dev *cdev);
 	int (*points_info)(struct sof_client_dev *cdev,
 			   struct sof_probe_point_desc **desc,
-			   size_t *num_desc);
+			   size_t *num_desc, enum sof_probe_info_type type);
+	int (*point_print)(struct sof_client_dev *cdev, char *buf, size_t size,
+			   struct sof_probe_point_desc *desc);
 	int (*points_add)(struct sof_client_dev *cdev,
 			  struct sof_probe_point_desc *desc,
 			  size_t num_desc);
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 4c79513..2dbfc76 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -45,13 +45,30 @@ struct sof_state_event_entry {
 	struct list_head list;
 };
 
+/**
+ * struct sof_client_dev_entry - client device entry for internal management use
+ * @sdev:	pointer to SOF core device struct
+ * @list:	item in SOF core client dev list
+ * @client_dev: SOF client device
+ */
+struct sof_client_dev_entry {
+	struct snd_sof_dev *sdev;
+	struct list_head list;
+
+	struct sof_client_dev client_dev;
+};
+
+#define cdev_to_centry(cdev) \
+	container_of(cdev, struct sof_client_dev_entry, client_dev)
+
 static void sof_client_auxdev_release(struct device *dev)
 {
 	struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
 	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+	struct sof_client_dev_entry *centry = cdev_to_centry(cdev);
 
 	kfree(cdev->auxdev.dev.platform_data);
-	kfree(cdev);
+	kfree(centry);
 }
 
 static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
@@ -208,15 +225,18 @@ void sof_unregister_clients(struct snd_sof_dev *sdev)
 int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
 			    const void *data, size_t size)
 {
+	struct sof_client_dev_entry *centry;
 	struct auxiliary_device *auxdev;
 	struct sof_client_dev *cdev;
 	int ret;
 
-	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
-	if (!cdev)
+	centry = kzalloc(sizeof(*centry), GFP_KERNEL);
+	if (!centry)
 		return -ENOMEM;
 
-	cdev->sdev = sdev;
+	cdev = &centry->client_dev;
+
+	centry->sdev = sdev;
 	auxdev = &cdev->auxdev;
 	auxdev->name = name;
 	auxdev->dev.parent = sdev->dev;
@@ -246,7 +266,7 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
 
 	/* add to list of SOF client devices */
 	mutex_lock(&sdev->ipc_client_mutex);
-	list_add(&cdev->list, &sdev->ipc_client_list);
+	list_add(&centry->list, &sdev->ipc_client_list);
 	mutex_unlock(&sdev->ipc_client_mutex);
 
 	return 0;
@@ -255,7 +275,7 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
 	kfree(cdev->auxdev.dev.platform_data);
 
 err_dev_add_data:
-	kfree(cdev);
+	kfree(centry);
 
 	return ret;
 }
@@ -263,7 +283,7 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, "SND_SOC_SOF_CLIENT");
 
 void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
 {
-	struct sof_client_dev *cdev;
+	struct sof_client_dev_entry *centry;
 
 	mutex_lock(&sdev->ipc_client_mutex);
 
@@ -271,9 +291,11 @@ void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 i
 	 * sof_client_auxdev_release() will be invoked to free up memory
 	 * allocations through put_device()
 	 */
-	list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+	list_for_each_entry(centry, &sdev->ipc_client_list, list) {
+		struct sof_client_dev *cdev = &centry->client_dev;
+
 		if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
-			list_del(&cdev->list);
+			list_del(&centry->list);
 			auxiliary_device_delete(&cdev->auxdev);
 			auxiliary_device_uninit(&cdev->auxdev);
 			break;
@@ -287,15 +309,17 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT");
 int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
 			      void *reply_data, size_t reply_bytes)
 {
-	if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+	if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
 		struct sof_ipc_cmd_hdr *hdr = ipc_msg;
 
-		return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size,
+		return sof_ipc_tx_message(sdev->ipc, ipc_msg, hdr->size,
 					  reply_data, reply_bytes);
-	} else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+	} else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
 		struct sof_ipc4_msg *msg = ipc_msg;
 
-		return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size,
+		return sof_ipc_tx_message(sdev->ipc, ipc_msg, msg->data_size,
 					  reply_data, reply_bytes);
 	}
 
@@ -305,16 +329,18 @@ EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, "SND_SOC_SOF_CLIENT");
 
 int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf)
 {
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
 	if (IS_ENABLED(CONFIG_SND_SOC_SOF_IPC3) &&
-	    cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+	    sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
 		struct sof_ipc_cmd_hdr *hdr = ipc_msg;
 
 		if (hdr->size < sizeof(hdr)) {
-			dev_err(cdev->sdev->dev, "The received message size is invalid\n");
+			dev_err(sdev->dev, "The received message size is invalid\n");
 			return -EINVAL;
 		}
 
-		sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf);
+		sof_ipc3_do_rx_work(sdev, ipc_msg, msg_buf);
 		return 0;
 	}
 
@@ -325,16 +351,17 @@ EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, "SND_SOC_SOF_CLIENT");
 int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
 				bool set)
 {
-	if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+	if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
 		struct sof_ipc_cmd_hdr *hdr = ipc_msg;
 
-		return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg, hdr->size,
-					    set);
-	} else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+		return sof_ipc_set_get_data(sdev->ipc, ipc_msg, hdr->size, set);
+	} else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
 		struct sof_ipc4_msg *msg = ipc_msg;
 
-		return sof_ipc_set_get_data(cdev->sdev->ipc, ipc_msg,
-					    msg->data_size, set);
+		return sof_ipc_set_get_data(sdev->ipc, ipc_msg, msg->data_size,
+					    set);
 	}
 
 	return -EINVAL;
@@ -344,7 +371,7 @@ EXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, "SND_SOC_SOF_CLIENT");
 #ifdef CONFIG_SND_SOC_SOF_IPC4
 struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid)
 {
-	struct snd_sof_dev *sdev = c->sdev;
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(c);
 
 	if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4)
 		return sof_ipc4_find_module_by_uuid(sdev, uuid);
@@ -353,16 +380,31 @@ struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c,
 	return NULL;
 }
 EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, "SND_SOC_SOF_CLIENT");
+
+struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev,
+							  u32 module_id, int instance_id)
+{
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+	if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4)
+		return sof_ipc4_find_swidget_by_ids(sdev, module_id, instance_id);
+	dev_err(sdev->dev, "Only supported with IPC4\n");
+
+	return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_swidget_by_id, "SND_SOC_SOF_CLIENT");
 #endif
 
 int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
 {
 	const struct auxiliary_driver *adrv;
-	struct sof_client_dev *cdev;
+	struct sof_client_dev_entry *centry;
 
 	mutex_lock(&sdev->ipc_client_mutex);
 
-	list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+	list_for_each_entry(centry, &sdev->ipc_client_list, list) {
+		struct sof_client_dev *cdev = &centry->client_dev;
+
 		/* Skip devices without loaded driver */
 		if (!cdev->auxdev.dev.driver)
 			continue;
@@ -381,11 +423,13 @@ EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT");
 int sof_resume_clients(struct snd_sof_dev *sdev)
 {
 	const struct auxiliary_driver *adrv;
-	struct sof_client_dev *cdev;
+	struct sof_client_dev_entry *centry;
 
 	mutex_lock(&sdev->ipc_client_mutex);
 
-	list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+	list_for_each_entry(centry, &sdev->ipc_client_list, list) {
+		struct sof_client_dev *cdev = &centry->client_dev;
+
 		/* Skip devices without loaded driver */
 		if (!cdev->auxdev.dev.driver)
 			continue;
@@ -403,14 +447,18 @@ EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT");
 
 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
 {
-	return cdev->sdev->debugfs_root;
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+	return sdev->debugfs_root;
 }
 EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, "SND_SOC_SOF_CLIENT");
 
 /* DMA buffer allocation in client drivers must use the core SOF device */
 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
 {
-	return cdev->sdev->dev;
+	struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+	return sdev->dev;
 }
 EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, "SND_SOC_SOF_CLIENT");
 
@@ -498,10 +546,10 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
 	if (!callback)
 		return -EINVAL;
 
-	if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
+	if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) {
 		if (!(ipc_msg_type & SOF_GLB_TYPE_MASK))
 			return -EINVAL;
-	} else if (cdev->sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+	} else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
 		if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK))
 			return -EINVAL;
 	} else {
@@ -611,3 +659,11 @@ enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
 	return sdev->fw_state;
 }
 EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, "SND_SOC_SOF_CLIENT");
+
+struct snd_sof_dev *sof_client_dev_to_sof_dev(struct sof_client_dev *cdev)
+{
+	struct sof_client_dev_entry *centry = cdev_to_centry(cdev);
+
+	return centry->sdev;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_to_sof_dev, "SND_SOC_SOF_CLIENT");
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index b6ccc2c..1a9015e 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -18,19 +18,13 @@ struct sof_ipc4_fw_module;
 /**
  * struct sof_client_dev - SOF client device
  * @auxdev:	auxiliary device
- * @sdev:	pointer to SOF core device struct
- * @list:	item in SOF core client dev list
  * @data:	device specific data
  */
 struct sof_client_dev {
 	struct auxiliary_device auxdev;
-	struct snd_sof_dev *sdev;
-	struct list_head list;
 	void *data;
 };
 
-#define sof_client_dev_to_sof_dev(cdev)		((cdev)->sdev)
-
 #define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \
 	container_of(auxiliary_dev, struct sof_client_dev, auxdev)
 
@@ -47,6 +41,8 @@ int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
 				bool set);
 
 struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *u);
+struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev,
+							  u32 module_id, int instance_id);
 
 struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
 struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index abbb5ee..0f624d8 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -838,7 +838,11 @@ int sof_stream_pcm_close(struct snd_sof_dev *sdev,
 			 struct snd_pcm_substream *substream);
 
 /* SOF client support */
+struct sof_client_dev;
+
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
+struct snd_sof_dev *sof_client_dev_to_sof_dev(struct sof_client_dev *cdev);
+
 int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
 			    const void *data, size_t size);
 void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id);
@@ -849,6 +853,11 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev);
 int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state);
 int sof_resume_clients(struct snd_sof_dev *sdev);
 #else /* CONFIG_SND_SOC_SOF_CLIENT */
+static inline struct snd_sof_dev *
+sof_client_dev_to_sof_dev(struct sof_client_dev *cdev) {
+	return NULL;
+}
+
 static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name,
 					  u32 id, const void *data, size_t size)
 {