linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Add PM4125 audio codec driver
@ 2025-06-25 23:50 Alexey Klimov
  2025-06-25 23:50 ` [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec Alexey Klimov
                   ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-06-25 23:50 UTC (permalink / raw)
  To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla, Alexey Klimov

PMICs like PM4125 have in-built audio codec IC. The series here
adds support for this codec driver: DT bindings and codec driver
itself that consists mainly of two parts: soundwire devices and
codec part itself.

This audio codec can be found on platforms like QCM2290 and
on Qualcomm QRB2210 RB1 board.

We are working on this together with Srini
(srinivas.kandagatla@oss.qualcomm.com or srini@kernel.org)
so this is shared here as minimal working driver and more commits on
top of this are expected that will add and extend functionality.

Currently with this set the playback via line out or headset work.

This driver also has a bit limited support for concurrent playback,
since line out path is connected to left input channel.

Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
---
Alexey Klimov (3):
      dt-bindings: sound: add bindings for pm4125 audio codec
      dt-bindings: mfd: qcom,spmi-pmic: add pm4125 audio codec
      ASoC: codecs: add new pm4125 audio codec driver

 .../devicetree/bindings/mfd/qcom,spmi-pmic.yaml    |    4 +-
 .../bindings/sound/qcom,pm4125-codec.yaml          |  147 ++
 .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |   86 +
 sound/soc/codecs/Kconfig                           |   19 +
 sound/soc/codecs/Makefile                          |    8 +
 sound/soc/codecs/pm4125-sdw.c                      |  485 +++++
 sound/soc/codecs/pm4125.c                          | 1848 ++++++++++++++++++++
 sound/soc/codecs/pm4125.h                          |  375 ++++
 8 files changed, 2971 insertions(+), 1 deletion(-)
---
base-commit: 2ae2aaafb21454f4781c30734959cf223ab486ef
change-id: 20250626-pm4125_audio_codec_v1-02ca7a300ddc

Best regards,
-- 
Alexey Klimov <alexey.klimov@linaro.org>


^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-25 23:50 [PATCH 0/3] Add PM4125 audio codec driver Alexey Klimov
@ 2025-06-25 23:50 ` Alexey Klimov
  2025-06-26  1:30   ` Rob Herring (Arm)
  2025-06-26  6:13   ` Krzysztof Kozlowski
  2025-06-25 23:50 ` [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add " Alexey Klimov
  2025-06-25 23:50 ` [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver Alexey Klimov
  2 siblings, 2 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-06-25 23:50 UTC (permalink / raw)
  To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla, Alexey Klimov

The audio codec IC is found on Qualcomm PM4125/PM2250 PMIC.
It has TX and RX soundwire slave devices hence two files
are added.

Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
---
 .../bindings/sound/qcom,pm4125-codec.yaml          | 147 +++++++++++++++++++++
 .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |  86 ++++++++++++
 2 files changed, 233 insertions(+)

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 0000000000000000000000000000000000000000..1b6ce8d4397b4c1c048899bd2cc4d02318cc46c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
@@ -0,0 +1,147 @@
+# 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"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/spmi/spmi.h>
+
+    spmi {
+        #address-cells = <2>;
+        #size-cells = <0>;
+
+        pmic@0 {
+            compatible = "qcom,pm8916", "qcom,spmi-pmic";
+            reg = <0x0 SPMI_USID>;
+            #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>;
+        };
+    };
+
+    soundwire@a740000 {
+        reg = <0x0a740000 0x2000>;
+        #address-cells = <2>;
+        #size-cells = <0>;
+        pm4125_tx: audio-codec@0,3 {
+            compatible = "sdw20217010c00";
+            reg = <0 3>;
+            qcom,tx-port-mapping = <1 1>;
+        };
+    };
+...
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 0000000000000000000000000000000000000000..7241d2ab5dcf4a0d5f25a75acb33a335f93d3b5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
@@ -0,0 +1,86 @@
+# 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. This bindings is for the
+  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
+
+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>;
+        };
+    };
+
+    soundwire@a740000 {
+        reg = <0x0a740000 0x2000>;
+        #address-cells = <2>;
+        #size-cells = <0>;
+        pm4125_tx: codec@0,1 {
+            compatible = "sdw20217010c00";
+            reg = <0 1>;
+            qcom,tx-port-mapping = <1 1>;
+        };
+    };
+
+...

-- 
2.47.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add pm4125 audio codec
  2025-06-25 23:50 [PATCH 0/3] Add PM4125 audio codec driver Alexey Klimov
  2025-06-25 23:50 ` [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec Alexey Klimov
@ 2025-06-25 23:50 ` Alexey Klimov
  2025-06-26  8:48   ` Krzysztof Kozlowski
  2025-06-25 23:50 ` [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver Alexey Klimov
  2 siblings, 1 reply; 25+ messages in thread
From: Alexey Klimov @ 2025-06-25 23:50 UTC (permalink / raw)
  To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla, Alexey Klimov

PM4125 has audio codec hardware block. Add pattern for respecive node
so the devicetree for those blocks can be validated properly.

Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
---
 Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
index 078a6886f8b1e9ceb2187e988ce7c9514ff6dc2c..5718cfe7f4c2b696ee2700fafe8dc071c70a6476 100644
--- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
+++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
@@ -137,7 +137,9 @@ patternProperties:
 
   "^audio-codec@[0-9a-f]+$":
     type: object
-    $ref: /schemas/sound/qcom,pm8916-wcd-analog-codec.yaml#
+    oneOf:
+      - $ref: /schemas/sound/qcom,pm8916-wcd-analog-codec.yaml#
+      - $ref: /schemas/sound/qcom,pm4125-codec.yaml#
 
   "^battery@[0-9a-f]+$":
     type: object

-- 
2.47.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-25 23:50 [PATCH 0/3] Add PM4125 audio codec driver Alexey Klimov
  2025-06-25 23:50 ` [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec Alexey Klimov
  2025-06-25 23:50 ` [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add " Alexey Klimov
@ 2025-06-25 23:50 ` Alexey Klimov
  2025-06-26  6:19   ` Krzysztof Kozlowski
                     ` (2 more replies)
  2 siblings, 3 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-06-25 23:50 UTC (permalink / raw)
  To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla, Alexey Klimov

The audio codec is found in Qualcomm PM2250/PM4125 PMICs and is used on
platforms like Qualcomm QCM2290. It has soundwire interface and
corresponding RX and TX slave devices.

It has only two input channels: HPH left and right. The line output (LO)
is linked to HPHL so the hardware has some limitations regarding concurrent
playback via HPH and LO for instance.

Not all functionality is implemented and there are some number of
placeholders. Mostly output functionality like analog and digital mics is
missing at this point and implemented in a possible minimal way.
The codec driver also uses WCD MBCH framework. The MBHC functionality is
also implemented in a minimalistic way to enable IRQs.

Despite all limitations in its current state the line out and HPH playback
work.

Cc: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
---
 sound/soc/codecs/Kconfig      |   19 +
 sound/soc/codecs/Makefile     |    8 +
 sound/soc/codecs/pm4125-sdw.c |  485 +++++++++++
 sound/soc/codecs/pm4125.c     | 1848 +++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/pm4125.h     |  375 +++++++++
 5 files changed, 2735 insertions(+)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 6d7e4725d89cd33647770e4f2c4e81445b8335ce..69b08021d165f83f4a7ca18e476cfc7e2473f490 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -297,6 +297,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_WCD937X_SDW
 	imply SND_SOC_WCD938X_SDW
 	imply SND_SOC_WCD939X_SDW
+	imply SND_SOC_PM4125_SDW
 	imply SND_SOC_LPASS_MACRO_COMMON
 	imply SND_SOC_LPASS_RX_MACRO
 	imply SND_SOC_LPASS_TX_MACRO
@@ -2316,6 +2317,24 @@ config SND_SOC_WCD939X_SDW
 	  The WCD9390/9395 is a audio codec IC Integrated in
 	  Qualcomm SoCs like SM8650.
 
+config SND_SOC_PM4125
+	depends on SND_SOC_PM4125_SDW
+	tristate
+	depends on SOUNDWIRE || !SOUNDWIRE
+	select SND_SOC_WCD_CLASSH
+
+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_WL1273
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a68c3d192a1b6ccec513c6bc447c29be532ea70c..e993cc9803c4da60daf230a8181673b45c06aa5b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -348,6 +348,8 @@ snd-soc-wcd938x-y := wcd938x.o
 snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
 snd-soc-wcd939x-y := wcd939x.o
 snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
+snd-soc-pm4125-y := pm4125.o
+snd-soc-pm4125-sdw-y := pm4125-sdw.o
 snd-soc-wl1273-y := wl1273.o
 snd-soc-wm-adsp-y := wm_adsp.o
 snd-soc-wm0010-y := wm0010.o
@@ -779,6 +781,12 @@ ifdef CONFIG_SND_SOC_WCD939X_SDW
 # 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_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_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
diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
new file mode 100644
index 0000000000000000000000000000000000000000..d09c4f5fb3b7b7918a1a0c25750046b212f1063f
--- /dev/null
+++ b/sound/soc/codecs/pm4125-sdw.c
@@ -0,0 +1,485 @@
+// 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)),
+	WCD_SDW_CH(PM4125_COMP_L, PM4125_COMP_PORT, BIT(0)),
+	WCD_SDW_CH(PM4125_COMP_R, PM4125_COMP_PORT, BIT(1)),
+};
+
+static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
+	WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_PORT, BIT(0)),
+	WCD_SDW_CH(PM4125_DMIC0, PM4125_DMIC_0_3_MBHC_PORT, BIT(0)),
+	WCD_SDW_CH(PM4125_DMIC1, PM4125_DMIC_0_3_MBHC_PORT, BIT(1)),
+	WCD_SDW_CH(PM4125_DMIC2, PM4125_DMIC_0_3_MBHC_PORT, BIT(2)),
+};
+
+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)
+{
+	if (reg > PM4125_ANA_BASE_ADDR &&
+	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
+		return pm4125_reg_access_analog[PM4125_REG(reg)] & WR_REG;
+
+	if (reg > PM4125_DIG_BASE_ADDR &&
+	    reg < PM4125_DIGITAL_REGISTERS_MAX_SIZE)
+		return pm4125_reg_access_digital[PM4125_REG(reg)] & WR_REG;
+
+	return false;
+}
+
+static bool pm4125_readable_register(struct device *dev, unsigned int reg)
+{
+	if (reg > PM4125_ANA_BASE_ADDR &&
+	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
+		return pm4125_reg_access_analog[PM4125_REG(reg)] & RD_REG;
+
+	if (reg > PM4125_DIG_BASE_ADDR &&
+	    reg < PM4125_DIGITAL_REGISTERS_MAX_SIZE)
+		return pm4125_reg_access_digital[PM4125_REG(reg)] & RD_REG;
+
+	return pm4125_rdwr_register(dev, reg);
+}
+
+static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
+{
+
+	if (reg > PM4125_ANA_BASE_ADDR &&
+	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
+		if ((pm4125_reg_access_analog[PM4125_REG(reg)] & RD_REG) &&
+		     !(pm4125_reg_access_analog[PM4125_REG(reg)] & WR_REG))
+			return true;
+
+	if (reg > PM4125_DIG_BASE_ADDR &&
+	    reg < PM4125_DIGITAL_REGISTERS_MAX_SIZE)
+		if ((pm4125_reg_access_digital[PM4125_REG(reg)] & RD_REG) &&
+		     !(pm4125_reg_access_digital[PM4125_REG(reg)] & WR_REG))
+			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_mark_last_busy(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 start 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 0000000000000000000000000000000000000000..58435b9c75865b06b344c6d79800aa9b4bac3abd
--- /dev/null
+++ b/sound/soc/codecs/pm4125.c
@@ -0,0 +1,1848 @@
+// 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_params.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "wcd-mbhc-v2.h"
+#include "pm4125.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,
+};
+
+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 regulator_bulk_data supplies[PM4125_MAX_BULK_SUPPLY];
+	struct regulator *buck_supply;
+	struct snd_soc_jack *jack;
+	unsigned long status_mask;
+	s32 micb_ref[PM4125_MAX_MICBIAS];
+	s32 pullup_ref[PM4125_MAX_MICBIAS];
+	u32 hph_mode;
+	int ear_rx_path;
+	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 rx_clk_cnt;
+	atomic_t ana_clk_count;
+};
+
+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;
+
+	if (data)
+		pm4125 = (struct pm4125_priv *)data;
+	else
+		return IRQ_HANDLED;
+
+	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 const 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,
+	.irq_drv_data = NULL,
+};
+
+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, 0x03, 0x00);
+
+	/* Enable surge protection */
+	regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, 0xC0, 0xC0);
+
+	/* Disable mic bias pull down */
+	regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01, 0x00);
+}
+
+static int pm4125_global_mbias_disable(struct snd_soc_component *component)
+{
+	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
+				      0x10, 0x00);
+	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
+				      0x20, 0x00);
+	return 0;
+}
+
+static int pm4125_global_mbias_enable(struct snd_soc_component *component)
+{
+	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
+				      0x20, 0x20);
+	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
+				      0x10, 0x10);
+	usleep_range(1000, 1100);
+	return 0;
+}
+
+static int pm4125_rx_clk_enable(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	if (atomic_read(&pm4125->rx_clk_cnt))
+		return 0;
+
+	snd_soc_component_update_bits(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      0x10, 0x10);
+	snd_soc_component_update_bits(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      0x20, 0x20);
+	usleep_range(5000, 5100);
+
+	pm4125_global_mbias_enable(component);
+
+	snd_soc_component_update_bits(component, PM4125_ANA_HPHPA_FSM_CLK,
+				      0x7F, 0x11);
+	snd_soc_component_update_bits(component, PM4125_ANA_HPHPA_FSM_CLK,
+				      0x80, 0x80);
+	snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL,
+				      0x07, 0x06);
+	snd_soc_component_update_bits(component, PM4125_ANA_NCP_EN,
+				      0x01, 0x01);
+	usleep_range(500, 510);
+
+	atomic_inc(&pm4125->rx_clk_cnt);
+
+	return 0;
+}
+
+static int pm4125_rx_clk_disable(struct snd_soc_component *component)
+{
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	if (!atomic_read(&pm4125->rx_clk_cnt)) {
+		dev_err(component->dev, "clk already disabled\n");
+		return 0;
+	}
+
+	atomic_dec(&pm4125->rx_clk_cnt);
+
+	snd_soc_component_update_bits(component,
+				      PM4125_ANA_HPHPA_FSM_CLK,
+				      0x80, 0x00);
+	snd_soc_component_update_bits(component,
+				      PM4125_ANA_HPHPA_FSM_CLK,
+				      0x7F, 0x00);
+	snd_soc_component_update_bits(component,
+				      PM4125_ANA_NCP_EN,
+				      0x01, 0x00);
+	snd_soc_component_update_bits(component,
+				      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      0x20, 0x00);
+	snd_soc_component_update_bits(component,
+				      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+				      0x10, 0x00);
+	pm4125_global_mbias_disable(component);
+
+	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:
+		pm4125_rx_clk_enable(component);
+		snd_soc_component_update_bits(component,
+					      PM4125_ANA_HPHPA_CNP_CTL_1,
+					      0x02, 0x02);
+
+		snd_soc_component_update_bits(component,
+					      PM4125_SWR_HPHPA_HD2,
+					      0x38, 0x38);
+
+		set_bit(HPH_COMP_DELAY, &pm4125->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+
+		if (pm4125->comp1_enable) {
+			snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x02, 0x02);
+
+			if (pm4125->comp2_enable)
+				snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x01, 0x01);
+			/*
+			 * 5ms sleep is required after COMP is enabled as per
+			 * HW requirement
+			 */
+			if (test_bit(HPH_COMP_DELAY, &pm4125->status_mask)) {
+				usleep_range(5000, 5100);
+				clear_bit(HPH_COMP_DELAY, &pm4125->status_mask);
+			}
+		} else {
+			snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x02, 0x00);
+		}
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX0_CTL,
+					      0x80, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      0x04, 0x04);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      0x01, 0x01);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      0x01, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      0x04, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX0_CTL,
+					      0x80, 0x80);
+		if (pm4125->comp1_enable)
+			snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x02, 0x00);
+		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:
+		pm4125_rx_clk_enable(component);
+
+		snd_soc_component_update_bits(component,
+					      PM4125_ANA_HPHPA_CNP_CTL_1,
+					      0x02, 0x02);
+		snd_soc_component_update_bits(component,
+					      PM4125_SWR_HPHPA_HD2,
+					      0x07, 0x07);
+
+		set_bit(HPH_COMP_DELAY, &pm4125->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (pm4125->comp2_enable) {
+			snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x01, 0x01);
+
+			if (pm4125->comp1_enable)
+				snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x02, 0x02);
+			/*
+			 * 5ms sleep is required after COMP is enabled
+			 * as per HW requirement
+			 */
+			if (test_bit(HPH_COMP_DELAY, &pm4125->status_mask)) {
+				usleep_range(5000, 5100);
+				clear_bit(HPH_COMP_DELAY, &pm4125->status_mask);
+			}
+		} else {
+			snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x01, 0x00);
+		}
+
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX1_CTL,
+					      0x80, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      0x08, 0x08);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      0x02, 0x02);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      0x02, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      0x08, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX1_CTL,
+					      0x80, 0x80);
+
+		if (pm4125->comp2_enable)
+			snd_soc_component_update_bits(component,
+						PM4125_DIG_SWR_CDC_COMP_CTL_0,
+						0x01, 0x00);
+		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:
+		pm4125_rx_clk_enable(component);
+
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX0_CTL,
+					      0x80, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      0x01, 0x01);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      0x04, 0x04);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+					      0x01, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+					      0x04, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_RX0_CTL,
+					      0x80, 0x80);
+		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);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		usleep_range(200, 210);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL1,
+					      0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		}
+
+		enable_irq(pm4125->hphr_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		}
+
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL1,
+					      0x03, 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);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		usleep_range(200, 210);
+		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL0,
+					      0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		}
+
+		enable_irq(pm4125->hphl_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
+		}
+
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL0,
+					      0x03, 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);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	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,
+					      0x40, 0x40);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL0,
+					      0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5010);
+
+		snd_soc_component_update_bits(component,
+					      PM4125_ANA_COMBOPA_CTL_4,
+					      0x0F, 0x04);
+		enable_irq(pm4125->hphl_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(2000, 2010);
+
+		snd_soc_component_update_bits(component,
+					      PM4125_ANA_COMBOPA_CTL,
+					      0x40, 0x00);
+		usleep_range(5000, 5100);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL0,
+					      0x03, 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);
+	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+	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,
+					      0x40, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL0,
+					      0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5010);
+		snd_soc_component_update_bits(component,
+					      PM4125_ANA_COMBOPA_CTL_4,
+					      0x0F, 0x04);
+
+		enable_irq(pm4125->hphl_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5010);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_PDM_WD_CTL0,
+					      0x03, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_enable_rx1(struct snd_soc_dapm_widget *w,
+			     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+					snd_soc_dapm_to_component(w->dapm);
+
+	if (event == SND_SOC_DAPM_POST_PMD)
+		pm4125_rx_clk_disable(component);
+
+	return 0;
+}
+
+static int pm4125_enable_rx2(struct snd_soc_dapm_widget *w,
+			     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+					snd_soc_dapm_to_component(w->dapm);
+
+	if (event == SND_SOC_DAPM_POST_PMD)
+		pm4125_rx_clk_disable(component);
+
+	return 0;
+}
+
+static int pm4125_get_micb_vout_ctl_val(u32 micb_mv)
+{
+	if (micb_mv < 1600 || micb_mv > 2850) {
+		pr_err("Unsupported micbias voltage (%u mV)\n", 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)
+{
+	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;
+
+	switch (w->shift) {
+	case 0:
+	case 1:
+		dmic_clk_reg = PM4125_DIG_SWR_CDC_DMIC1_CTL;
+		break;
+	default:
+		dev_err(component->dev, "Invalid DMIC selection\n");
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_AMIC_CTL,
+					      0x02, 0x00);
+		snd_soc_component_update_bits(component,
+					      dmic_clk_reg,
+					      0x08, 0x08);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+					      dmic_clk_reg,
+					      0x08, 0x00);
+		snd_soc_component_update_bits(component,
+					      PM4125_DIG_SWR_CDC_AMIC_CTL,
+					      0x02, 0x02);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_micbias_control(struct snd_soc_component *component,
+				  int micb_num, int req, bool is_dapm)
+{
+	return 0;
+}
+
+static int __pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+					 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_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_DISABLE, true);
+		break;
+	}
+
+	return 0;
+}
+
+static int pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	return __pm4125_codec_enable_micbias(w, event);
+}
+
+static int __pm4125_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+						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_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+					      struct snd_kcontrol *kcontrol,
+					      int event)
+{
+	return __pm4125_codec_enable_micbias_pullup(w, event);
+}
+
+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 const struct snd_kcontrol_new hph_type_detect_controls[] = {
+	SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, NULL, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, NULL, NULL),
+	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, NULL, NULL),
+};
+
+static void pm4125_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+					  bool enable)
+{
+	snd_soc_component_update_bits(component, PM4125_ANA_MBHC_ELECT, 0x01,
+				      enable ? 0x01 : 0x00);
+}
+
+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,
+					0xfc, vth << 2);
+	}
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+	.clk_setup = NULL,
+	.mbhc_bias = pm4125_mbhc_mbhc_bias_control,
+	.set_btn_thr = pm4125_mbhc_program_btn_thr,
+	.micbias_enable_status = NULL,
+	.hph_pull_up_control_v2 = NULL,
+	.mbhc_micbias_control = NULL,
+	.mbhc_micb_ramp_control = NULL,
+	.mbhc_micb_ctrl_thr_mic = NULL,
+	.compute_impedance = NULL,
+	.mbhc_gnd_det_ctrl = NULL,
+	.hph_pull_down_ctrl = NULL,
+	.mbhc_moisture_config = NULL,
+	.mbhc_get_moisture_status = NULL,
+	.mbhc_moisture_polling_ctrl = NULL,
+	.mbhc_moisture_detect_en = NULL,
+};
+
+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);
+
+	snd_soc_add_component_controls(component, impedance_detect_controls,
+				       ARRAY_SIZE(impedance_detect_controls));
+	snd_soc_add_component_controls(component, hph_type_detect_controls,
+				       ARRAY_SIZE(hph_type_detect_controls));
+	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", SND_SOC_NOPM, 0, 1, 0,
+		       pm4125_get_compander, pm4125_set_compander),
+	SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 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("LO Switch", PM4125_LO, 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),
+	SOC_SINGLE_EXT("DMIC0 Switch", PM4125_DMIC0, 1, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+	SOC_SINGLE_EXT("DMIC1 Switch", PM4125_DMIC1, 1, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+	SOC_SINGLE_EXT("MBHC Switch", PM4125_MBHC, 1, 1, 0,
+		       pm4125_get_swr_port, pm4125_set_swr_port),
+	SOC_SINGLE_EXT("DMIC2 Switch", PM4125_DMIC2, 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 adc3_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_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0,
+			     adc1_switch, ARRAY_SIZE(adc1_switch),
+			     NULL, SND_SOC_DAPM_PRE_PMU |
+			     SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 1, 0,
+			     adc2_switch, ARRAY_SIZE(adc2_switch),
+			     NULL, SND_SOC_DAPM_PRE_PMU |
+			     SND_SOC_DAPM_POST_PMD),
+
+	/* 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_PRE_PMD | 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_PRE_PMD | 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_PMU |
+			   SND_SOC_DAPM_PRE_PMD | 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_PMU |
+			   SND_SOC_DAPM_PRE_PMD | 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_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0,
+			     pm4125_enable_rx1, SND_SOC_DAPM_PRE_PMU |
+			     SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0,
+			     pm4125_enable_rx2, SND_SOC_DAPM_PRE_PMU |
+			     SND_SOC_DAPM_POST_PMD),
+
+	/* 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, SND_SOC_NOPM, 0, 0,
+			   pm4125_codec_enable_dmic,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
+			   pm4125_codec_enable_dmic,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* TX mixer widgets */
+	SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0,
+			     0, dmic1_switch, ARRAY_SIZE(dmic1_switch),
+			     NULL, SND_SOC_DAPM_PRE_PMU |
+			     SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 1,
+			     0, dmic2_switch, ARRAY_SIZE(dmic2_switch),
+			     NULL, SND_SOC_DAPM_PRE_PMU |
+			     SND_SOC_DAPM_POST_PMD),
+
+	/* 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" },
+	{ "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" },
+	{ "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" },
+
+	{ "RDAC3", NULL, "RX1" },
+	{ "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 pm4125_priv *pm4125)
+{
+	int vout_ctl;
+
+	/* Set micbias voltage */
+	vout_ctl = pm4125_get_micb_vout_ctl_val(pm4125->micb1_mv);
+	if (vout_ctl < 0)
+		return -EINVAL;
+
+	regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
+			   0xF8, vout_ctl << 3);
+	return 0;
+}
+
+static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
+{
+	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;
+	}
+
+	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 interrupt */
+	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 bool pm4125_swap_gnd_mic(struct snd_soc_component *component)
+{
+	return true;
+}
+
+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 = "WCD 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 = "WCD 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);
+	int ret;
+
+	/* Give the SDW 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 slave with matching of node\n");
+		return -EINVAL;
+	}
+
+	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");
+		return -EINVAL;
+	}
+
+	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");
+		return -EINVAL;
+	}
+
+	/*
+	 * As TX is the main CSR reg interface, which should not be suspended first.
+	 * expicilty add the dependency link
+	 */
+	if (!device_link_add(pm4125->rxdev, pm4125->txdev,
+			     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
+		dev_err(dev, "Could not devlink TX and RX\n");
+		return -EINVAL;
+	}
+
+	if (!device_link_add(dev, pm4125->txdev,
+			     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
+		dev_err(dev, "Could not devlink WCD and TX\n");
+		return -EINVAL;
+	}
+
+	if (!device_link_add(dev, pm4125->rxdev,
+			     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
+		dev_err(dev, "Could not devlink WCD and RX\n");
+		return -EINVAL;
+	}
+
+	pm4125->regmap = dev_get_regmap(&pm4125->tx_sdw_dev->dev, NULL);
+	if (!pm4125->regmap) {
+		dev_err(dev, "could not get TX device regmap\n");
+		return -EINVAL;
+	}
+
+	ret = pm4125_irq_init(pm4125, dev);
+	if (ret) {
+		dev_err(dev, "IRQ init failed: %d\n", ret);
+		return ret;
+	}
+
+	pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
+	pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
+
+	ret = pm4125_set_micbias_data(pm4125);
+	if (ret < 0) {
+		dev_err(dev, "Bad micbias pdata\n");
+		return ret;
+	}
+
+	ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
+					 pm4125_dais, ARRAY_SIZE(pm4125_dais));
+	if (ret)
+		dev_err(dev, "Codec registration failed\n");
+
+	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) {
+		dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n");
+		return -ENODEV;
+	}
+	of_node_get(pm4125->rxnode);
+	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) {
+		dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n");
+			return -ENODEV;
+	}
+	of_node_get(pm4125->txnode);
+	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);
+
+	cfg = &pm4125->mbhc_cfg;
+	cfg->swap_gnd_mic = pm4125_swap_gnd_mic;
+
+	pm4125->supplies[0].supply = "vdd-io";
+	pm4125->supplies[1].supply = "vdd-cp";
+	pm4125->supplies[2].supply = "vdd-mic-bias";
+	pm4125->supplies[3].supply = "vdd-pa-vpos";
+
+	ret = devm_regulator_bulk_get(dev, PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get supplies\n");
+
+	ret = regulator_bulk_enable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+	if (ret) {
+		regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+		return dev_err_probe(dev, ret, "Failed to enable supplies\n");
+	}
+
+	pm4125_dt_parse_micbias_info(dev, pm4125);
+
+	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;
+
+	pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pm4125->spmi_regmap)
+		return -ENXIO;
+
+	pm4125_reset(pm4125);
+
+	wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
+
+	ret = pm4125_add_slave_components(pm4125, dev, &match);
+	if (ret)
+		goto err_disable_regulators;
+
+	ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
+	if (ret)
+		goto err_disable_regulators;
+
+	pm_runtime_set_autosuspend_delay(dev, 1000);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+
+err_disable_regulators:
+	regulator_bulk_disable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+	regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+
+	return ret;
+}
+
+static void pm4125_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+
+	component_master_del(&pdev->dev, &pm4125_comp_ops);
+
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_dont_use_autosuspend(dev);
+
+	regulator_bulk_disable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+	regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id pm4125_of_match[] = {
+	{ .compatible = "qcom,pm4125-codec" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pm4125_of_match);
+#endif
+
+static struct platform_driver pm4125_codec_driver = {
+	.probe = pm4125_probe,
+	.remove = pm4125_remove,
+	.driver = {
+		.name = "pm4125_codec",
+		.of_match_table = of_match_ptr(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 0000000000000000000000000000000000000000..2c5e8218202d92a0adc493413368991a406471b0
--- /dev/null
+++ b/sound/soc/codecs/pm4125.h
@@ -0,0 +1,375 @@
+/* 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_REG(reg)	((reg > PM4125_DIG_BASE_ADDR) ? \
+			 (reg - PM4125_DIG_BASE_ADDR) : \
+			 (reg - PM4125_ANA_BASE_ADDR))
+
+enum {
+	REG_NO_ACCESS,
+	RD_REG,
+	WR_REG,
+	RD_WR_REG
+};
+
+#define PM4125_ANA_MICBIAS_MICB_1_2_EN		(PM4125_ANA_BASE_ADDR+0x040)
+#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_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_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_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_VCTRL			(PM4125_ANA_BASE_ADDR+0x07C)
+#define PM4125_ANA_HPHPA_CNP_CTL_1		(PM4125_ANA_BASE_ADDR+0x083)
+#define PM4125_ANA_HPHPA_CNP_CTL_2		(PM4125_ANA_BASE_ADDR+0x084)
+#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_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_ANA_SURGE_EN			(PM4125_ANA_BASE_ADDR+0x097)
+#define PM4125_ANA_COMBOPA_CTL			(PM4125_ANA_BASE_ADDR+0x09B)
+#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_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_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_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_CDC_COMP_CTL_0		(PM4125_DIG_BASE_ADDR+0x04F)
+#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_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_CDC_DMIC_CTL		(PM4125_DIG_BASE_ADDR+0x06B)
+#define PM4125_DIG_SWR_CDC_DMIC1_CTL		(PM4125_DIG_BASE_ADDR+0x06C)
+#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_DIG_SWR_PDM_WD_CTL1		(PM4125_DIG_BASE_ADDR+0x071)
+#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_BULK_SUPPLY			4
+#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_PORT = 1,
+	PM4125_DMIC_0_3_MBHC_PORT,
+	PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_0_3_MBHC_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
+int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
+		    struct snd_pcm_substream *substream,
+		    struct snd_soc_dai *dai)
+{
+	return -EOPNOTSUPP;
+}
+
+int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
+			      struct snd_soc_dai *dai,
+			      void *stream, int direction)
+{
+	return -EOPNOTSUPP;
+}
+
+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,
+	PM4125_ADC3,
+	PM4125_DMIC0,
+	PM4125_DMIC1,
+	PM4125_MBHC,
+	PM4125_DMIC2,
+	PM4125_DMIC3,
+	PM4125_DMIC4,
+	PM4125_DMIC5,
+	PM4125_DMIC6,
+};
+
+enum pm4125_rx_sdw_channels {
+	PM4125_HPH_L,
+	PM4125_HPH_R,
+	PM4125_CLSH,
+	PM4125_COMP_L,
+	PM4125_COMP_R,
+	PM4125_LO,
+	PM4125_DSD_R,
+	PM4125_DSD_L,
+};
+
+const u8 pm4125_reg_access_analog[
+	PM4125_REG(PM4125_ANALOG_REGISTERS_MAX_SIZE)] = {
+		[PM4125_REG(PM4125_ANA_MICBIAS_MICB_1_2_EN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MICBIAS_MICB_3_EN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MICBIAS_LDO_1_SETTING)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MICBIAS_LDO_1_CTRL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_TX_AMIC1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_TX_AMIC2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_MECH)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_ELECT)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_ZDET)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_RESULT_1)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_RESULT_2)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_RESULT_3)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_BTN0_ZDET_VREF1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_BTN1_ZDET_VREF2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_BTN2_ZDET_VREF3)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_BTN3_ZDET_DBG_400)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_MICB2_RAMP)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_CTL_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_CTL_2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_PLUG_DETECT_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_ZDET_ANA_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_ZDET_RAMP_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_FSM_STATUS)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_ADC_RESULT)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_CTL_CLK)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBHC_ZDET_CALIB_RESULT)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_NCP_EN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_NCP_VCTRL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_CNP_CTL_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_CNP_CTL_2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_PA_STATUS)] = RD_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_FSM_CLK)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_L_GAIN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_R_GAIN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_HPHPA_SPARE_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_SWR_HPHPA_HD2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_SURGE_EN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_COMBOPA_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_COMBOPA_CTL_4)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_COMBOPA_CTL_5)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_RXLDO_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_ANA_MBIAS_EN)] = RD_WR_REG,
+	};
+
+
+const u8 pm4125_reg_access_digital[
+	PM4125_REG(PM4125_DIGITAL_REGISTERS_MAX_SIZE)] = {
+		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID0)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID1)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID2)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID3)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_SWR_TX_CLK_RATE)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RST_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_TOP_CLK_CFG)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX_CLK_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_TX_CLK_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_SWR_RST_EN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX_RST)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX0_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX1_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_COMP_CTL_0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX_DELAY_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX_GAIN_0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX_GAIN_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RX_GAIN_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_TX0_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_TX1_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_TX_RST)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_REQ0_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_REQ1_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_RST)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_AMIC_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_DMIC_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_DMIC1_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_DMIC1_RATE)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_PDM_WD_CTL0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_PDM_WD_CTL1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_MODE)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_MASK_0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_MASK_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_MASK_2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_STATUS_0)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_STATUS_1)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_STATUS_2)] = RD_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_CLEAR_0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_CLEAR_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_CLEAR_2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_LEVEL_0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_LEVEL_1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_INTR_LEVEL_2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_CONN_RX0_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_CDC_CONN_RX1_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_LOOP_BACK_MODE)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DRIVE_STRENGTH_0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DIG_DEBUG_CTL)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DIG_DEBUG_EN)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DEM_BYPASS_DATA0)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DEM_BYPASS_DATA1)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DEM_BYPASS_DATA2)] = RD_WR_REG,
+		[PM4125_REG(PM4125_DIG_SWR_DEM_BYPASS_DATA3)] = RD_WR_REG,
+	};
+
+#endif /* _PM4125_REGISTERS_H */

-- 
2.47.2


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-25 23:50 ` [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec Alexey Klimov
@ 2025-06-26  1:30   ` Rob Herring (Arm)
  2025-06-26 11:08     ` Alexey Klimov
  2025-06-26  6:13   ` Krzysztof Kozlowski
  1 sibling, 1 reply; 25+ messages in thread
From: Rob Herring (Arm) @ 2025-06-26  1:30 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, devicetree, Jaroslav Kysela, linux-arm-msm,
	Srinivas Kandagatla, Takashi Iwai, Mark Brown, linux-kernel,
	Krzysztof Kozlowski, Stephen Boyd, Conor Dooley, Lee Jones,
	linux-sound, Liam Girdwood, Dmitry Baryshkov


On Thu, 26 Jun 2025 00:50:29 +0100, Alexey Klimov wrote:
> The audio codec IC is found on Qualcomm PM4125/PM2250 PMIC.
> It has TX and RX soundwire slave devices hence two files
> are added.
> 
> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
> ---
>  .../bindings/sound/qcom,pm4125-codec.yaml          | 147 +++++++++++++++++++++
>  .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |  86 ++++++++++++
>  2 files changed, 233 insertions(+)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.example.dtb: pmic@0 (qcom,pm8916): audio-codec@f000: 'qcom,micbias1-microvolt', 'qcom,micbias2-microvolt', 'qcom,micbias3-microvolt', 'qcom,rx-device', 'qcom,tx-device', 'vdd-cp-supply', 'vdd-io-supply', 'vdd-mic-bias-supply', 'vdd-pa-vpos-supply' do not match any of the regexes: '^pinctrl-[0-9]+$'
	from schema $id: http://devicetree.org/schemas/mfd/qcom,spmi-pmic.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.example.dtb: pmic@0 (qcom,pm8916): audio-codec@f000:compatible:0: 'qcom,pm8916-wcd-analog-codec' was expected
	from schema $id: http://devicetree.org/schemas/mfd/qcom,spmi-pmic.yaml#

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250626-pm4125_audio_codec_v1-v1-1-e52933c429a0@linaro.org

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-25 23:50 ` [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec Alexey Klimov
  2025-06-26  1:30   ` Rob Herring (Arm)
@ 2025-06-26  6:13   ` Krzysztof Kozlowski
  2025-06-28 16:41     ` Alexey Klimov
  1 sibling, 1 reply; 25+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-26  6:13 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Thu, Jun 26, 2025 at 12:50:29AM +0100, Alexey Klimov wrote:
> The audio codec IC is found on Qualcomm PM4125/PM2250 PMIC.
> It has TX and RX soundwire slave devices hence two files
> are added.
> 
> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
> ---
>  .../bindings/sound/qcom,pm4125-codec.yaml          | 147 +++++++++++++++++++++
>  .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |  86 ++++++++++++
>  2 files changed, 233 insertions(+)
> 
> 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 0000000000000000000000000000000000000000..1b6ce8d4397b4c1c048899bd2cc4d02318cc46c9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
> @@ -0,0 +1,147 @@
> +# 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"

Keep consistent quotes, either ' or "

> +
> +additionalProperties: false

This has to unevaluatedProperties

> +
> +examples:
> +  - |
> +    #include <dt-bindings/spmi/spmi.h>
> +
> +    spmi {
> +        #address-cells = <2>;
> +        #size-cells = <0>;
> +
> +        pmic@0 {

pmic {

> +            compatible = "qcom,pm8916", "qcom,spmi-pmic";

Drop, you have warnings here.

> +            reg = <0x0 SPMI_USID>;

Drop

> +            #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 {

Drop this and next one.

> +        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>;
> +        };
> +    };
> +
> +    soundwire@a740000 {
> +        reg = <0x0a740000 0x2000>;
> +        #address-cells = <2>;
> +        #size-cells = <0>;
> +        pm4125_tx: audio-codec@0,3 {
> +            compatible = "sdw20217010c00";
> +            reg = <0 3>;
> +            qcom,tx-port-mapping = <1 1>;
> +        };
> +    };
> +...
> 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 0000000000000000000000000000000000000000..7241d2ab5dcf4a0d5f25a75acb33a335f93d3b5e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
> @@ -0,0 +1,86 @@
> +# 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: |

Drop |

> +  The audio codec IC found on Qualcomm PM4125/PM2250 PMICs.
> +  It has RX and TX Soundwire slave devices. This bindings is for the
> +  slave devices.

Last sentence is redundant and makes no sense. Codec has only slave
devices, so how this can be anything else than for 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

rx and tx are excluding, so this should be here encoded.

> +
> +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>;
> +        };
> +    };
> +
> +    soundwire@a740000 {
> +        reg = <0x0a740000 0x2000>;

One example is enough, they are the same.

> +        #address-cells = <2>;
> +        #size-cells = <0>;
> +        pm4125_tx: codec@0,1 {
> +            compatible = "sdw20217010c00";
> +            reg = <0 1>;
> +            qcom,tx-port-mapping = <1 1>;
> +        };
> +    };
> +
> +...
> 
> -- 
> 2.47.2
> 

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-25 23:50 ` [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver Alexey Klimov
@ 2025-06-26  6:19   ` Krzysztof Kozlowski
  2025-07-10 15:37     ` Alexey Klimov
  2025-06-26 11:56   ` Mark Brown
  2025-06-28 20:24   ` Srinivas Kandagatla
  2 siblings, 1 reply; 25+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-26  6:19 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Thu, Jun 26, 2025 at 12:50:31AM +0100, Alexey Klimov wrote:
> +
> +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) {
> +		dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n");
> +		return -ENODEV;
> +	}
> +	of_node_get(pm4125->rxnode);

Where  do you clean this up?

> +	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) {
> +		dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n");
> +			return -ENODEV;

Messed indent. This should be anyway just one line as always - return
dev_err_probe.

> +	}
> +	of_node_get(pm4125->txnode);

And this?

> +	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);
> +
> +	cfg = &pm4125->mbhc_cfg;
> +	cfg->swap_gnd_mic = pm4125_swap_gnd_mic;
> +
> +	pm4125->supplies[0].supply = "vdd-io";
> +	pm4125->supplies[1].supply = "vdd-cp";
> +	pm4125->supplies[2].supply = "vdd-mic-bias";
> +	pm4125->supplies[3].supply = "vdd-pa-vpos";
> +
> +	ret = devm_regulator_bulk_get(dev, PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to get supplies\n");
> +
> +	ret = regulator_bulk_enable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	if (ret) {
> +		regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);

Double free.

> +		return dev_err_probe(dev, ret, "Failed to enable supplies\n");
> +	}
> +
> +	pm4125_dt_parse_micbias_info(dev, pm4125);
> +
> +	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;
> +
> +	pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!pm4125->spmi_regmap)
> +		return -ENXIO;
> +
> +	pm4125_reset(pm4125);
> +
> +	wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
> +
> +	ret = pm4125_add_slave_components(pm4125, dev, &match);
> +	if (ret)
> +		goto err_disable_regulators;
> +
> +	ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
> +	if (ret)
> +		goto err_disable_regulators;
> +
> +	pm_runtime_set_autosuspend_delay(dev, 1000);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_idle(dev);
> +
> +	return 0;
> +
> +err_disable_regulators:
> +	regulator_bulk_disable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +
> +	return ret;
> +}
> +
> +static void pm4125_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
> +
> +	component_master_del(&pdev->dev, &pm4125_comp_ops);
> +
> +	pm_runtime_disable(dev);
> +	pm_runtime_set_suspended(dev);
> +	pm_runtime_dont_use_autosuspend(dev);
> +
> +	regulator_bulk_disable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +}
> +
> +#if defined(CONFIG_OF)
> +static const struct of_device_id pm4125_of_match[] = {
> +	{ .compatible = "qcom,pm4125-codec" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
> +#endif
> +
> +static struct platform_driver pm4125_codec_driver = {
> +	.probe = pm4125_probe,
> +	.remove = pm4125_remove,
> +	.driver = {
> +		.name = "pm4125_codec",
> +		.of_match_table = of_match_ptr(pm4125_of_match),

Drop of_match_ptr and #if. We just removed it (or trying to )
everywhere, so why re-introducing it...

> +		.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 0000000000000000000000000000000000000000..2c5e8218202d92a0adc493413368991a406471b0
> --- /dev/null
> +++ b/sound/soc/codecs/pm4125.h
> @@ -0,0 +1,375 @@
> +/* 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_REG(reg)	((reg > PM4125_DIG_BASE_ADDR) ? \
> +			 (reg - PM4125_DIG_BASE_ADDR) : \
> +			 (reg - PM4125_ANA_BASE_ADDR))
> +
> +enum {
> +	REG_NO_ACCESS,
> +	RD_REG,
> +	WR_REG,
> +	RD_WR_REG
> +};
> +
> +#define PM4125_ANA_MICBIAS_MICB_1_2_EN		(PM4125_ANA_BASE_ADDR+0x040)
> +#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_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_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_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_VCTRL			(PM4125_ANA_BASE_ADDR+0x07C)
> +#define PM4125_ANA_HPHPA_CNP_CTL_1		(PM4125_ANA_BASE_ADDR+0x083)
> +#define PM4125_ANA_HPHPA_CNP_CTL_2		(PM4125_ANA_BASE_ADDR+0x084)
> +#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_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_ANA_SURGE_EN			(PM4125_ANA_BASE_ADDR+0x097)
> +#define PM4125_ANA_COMBOPA_CTL			(PM4125_ANA_BASE_ADDR+0x09B)
> +#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_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_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_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_CDC_COMP_CTL_0		(PM4125_DIG_BASE_ADDR+0x04F)
> +#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_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_CDC_DMIC_CTL		(PM4125_DIG_BASE_ADDR+0x06B)
> +#define PM4125_DIG_SWR_CDC_DMIC1_CTL		(PM4125_DIG_BASE_ADDR+0x06C)
> +#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_DIG_SWR_PDM_WD_CTL1		(PM4125_DIG_BASE_ADDR+0x071)
> +#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_BULK_SUPPLY			4
> +#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_PORT = 1,
> +	PM4125_DMIC_0_3_MBHC_PORT,
> +	PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_0_3_MBHC_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
> +int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
> +		    struct snd_pcm_substream *substream,
> +		    struct snd_soc_dai *dai)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
> +			      struct snd_soc_dai *dai,
> +			      void *stream, int direction)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +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,
> +	PM4125_ADC3,
> +	PM4125_DMIC0,
> +	PM4125_DMIC1,
> +	PM4125_MBHC,
> +	PM4125_DMIC2,
> +	PM4125_DMIC3,
> +	PM4125_DMIC4,
> +	PM4125_DMIC5,
> +	PM4125_DMIC6,
> +};
> +
> +enum pm4125_rx_sdw_channels {
> +	PM4125_HPH_L,
> +	PM4125_HPH_R,
> +	PM4125_CLSH,
> +	PM4125_COMP_L,
> +	PM4125_COMP_R,
> +	PM4125_LO,
> +	PM4125_DSD_R,
> +	PM4125_DSD_L,
> +};
> +
> +const u8 pm4125_reg_access_analog[

No, you cannot have data defined in the header. This is neither style of
C, nor Linux kernel, nor makes any sense. What if this will be included
by some other unit? This is some terrible downstream style.

Heh... you actually do include it twice, so you would see all the
duplicated data for no reason at all.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add pm4125 audio codec
  2025-06-25 23:50 ` [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add " Alexey Klimov
@ 2025-06-26  8:48   ` Krzysztof Kozlowski
  2025-06-28 16:42     ` Alexey Klimov
  0 siblings, 1 reply; 25+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-26  8:48 UTC (permalink / raw)
  To: Alexey Klimov, Srinivas Kandagatla, Liam Girdwood, Mark Brown,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla

On 26/06/2025 01:50, Alexey Klimov wrote:
> PM4125 has audio codec hardware block. Add pattern for respecive node
> so the devicetree for those blocks can be validated properly.
> 
> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>

Remember to ALWAYS explain the dependencies between patches (merging
strategy), because this now creates impression is independent patch. It
is not and should be squashed into previous.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-26  1:30   ` Rob Herring (Arm)
@ 2025-06-26 11:08     ` Alexey Klimov
  0 siblings, 0 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-06-26 11:08 UTC (permalink / raw)
  To: Rob Herring (Arm)
  Cc: Srinivas Kandagatla, devicetree, Jaroslav Kysela, linux-arm-msm,
	Srinivas Kandagatla, Takashi Iwai, Mark Brown, linux-kernel,
	Krzysztof Kozlowski, Stephen Boyd, Conor Dooley, Lee Jones,
	linux-sound, Liam Girdwood, Dmitry Baryshkov

On Thu Jun 26, 2025 at 2:30 AM BST, Rob Herring (Arm) wrote:
>
> On Thu, 26 Jun 2025 00:50:29 +0100, Alexey Klimov wrote:
>> The audio codec IC is found on Qualcomm PM4125/PM2250 PMIC.
>> It has TX and RX soundwire slave devices hence two files
>> are added.
>> 
>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>> ---
>>  .../bindings/sound/qcom,pm4125-codec.yaml          | 147 +++++++++++++++++++++
>>  .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |  86 ++++++++++++
>>  2 files changed, 233 insertions(+)
>> 
>
> My bot found errors running 'make dt_binding_check' on your patch:
>
> yamllint warnings/errors:
>
> dtschema/dtc warnings/errors:
> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.example.dtb: pmic@0 (qcom,pm8916): audio-codec@f000: 'qcom,micbias1-microvolt', 'qcom,micbias2-microvolt', 'qcom,micbias3-microvolt', 'qcom,rx-device', 'qcom,tx-device', 'vdd-cp-supply', 'vdd-io-supply', 'vdd-mic-bias-supply', 'vdd-pa-vpos-supply' do not match any of the regexes: '^pinctrl-[0-9]+$'
> 	from schema $id: http://devicetree.org/schemas/mfd/qcom,spmi-pmic.yaml#
> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.example.dtb: pmic@0 (qcom,pm8916): audio-codec@f000:compatible:0: 'qcom,pm8916-wcd-analog-codec' was expected
> 	from schema $id: http://devicetree.org/schemas/mfd/qcom,spmi-pmic.yaml#
>
> doc reference errors (make refcheckdocs):
>
> See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250626-pm4125_audio_codec_v1-v1-1-e52933c429a0@linaro.org

The second patch in the series deals with that. Reordering these two patches
doesn't seem to make a lot of sense so I guess squashing it in here is
a way to go.

Thanks,
Alexey


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-25 23:50 ` [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver Alexey Klimov
  2025-06-26  6:19   ` Krzysztof Kozlowski
@ 2025-06-26 11:56   ` Mark Brown
  2025-07-01 19:35     ` Alexey Klimov
  2025-06-28 20:24   ` Srinivas Kandagatla
  2 siblings, 1 reply; 25+ messages in thread
From: Mark Brown @ 2025-06-26 11:56 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

[-- Attachment #1: Type: text/plain, Size: 2031 bytes --]

On Thu, Jun 26, 2025 at 12:50:31AM +0100, Alexey Klimov wrote:

> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -297,6 +297,7 @@ config SND_SOC_ALL_CODECS
>  	imply SND_SOC_WCD937X_SDW
>  	imply SND_SOC_WCD938X_SDW
>  	imply SND_SOC_WCD939X_SDW
> +	imply SND_SOC_PM4125_SDW
>  	imply SND_SOC_LPASS_MACRO_COMMON
>  	imply SND_SOC_LPASS_RX_MACRO
>  	imply SND_SOC_LPASS_TX_MACRO

Please keep this file sorted, there's obviously been some things missed
but please don't make it worse.

> +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

Other drivers sort this out in Kconfig, do as they do.

> +static int pm4125_micbias_control(struct snd_soc_component *component,
> +				  int micb_num, int req, bool is_dapm)
> +{
> +	return 0;
> +}

Why have this empty function which is only called from within the
driver?  At best it's making the callers look like they do something.

> +static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
> +{
> +	return IRQ_HANDLED;
> +}

Why bother regisering for the interrupt at all if you're just going to
ignore it?

> +#if defined(CONFIG_OF)
> +static const struct of_device_id pm4125_of_match[] = {
> +	{ .compatible = "qcom,pm4125-codec" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
> +#endif

Why does this compatible exist?  If the driver is instantiated from a
as a Linux software contruct it shouldn't appear in the DT.

> +const u8 pm4125_reg_access_digital[
> +	PM4125_REG(PM4125_DIGITAL_REGISTERS_MAX_SIZE)] = {
> +		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID0)] = RD_REG,
> +		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID1)] = RD_REG,
> +		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID2)] = RD_REG,

Data tables like this shouldn't be in headers, they should be in C
files.  At worst you might end up with duplicate copies in the object
code.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-26  6:13   ` Krzysztof Kozlowski
@ 2025-06-28 16:41     ` Alexey Klimov
  2025-06-29 14:59       ` Dmitry Baryshkov
  2025-06-30  8:21       ` Krzysztof Kozlowski
  0 siblings, 2 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-06-28 16:41 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Thu Jun 26, 2025 at 7:13 AM BST, Krzysztof Kozlowski wrote:
> On Thu, Jun 26, 2025 at 12:50:29AM +0100, Alexey Klimov wrote:
>> The audio codec IC is found on Qualcomm PM4125/PM2250 PMIC.
>> It has TX and RX soundwire slave devices hence two files
>> are added.
>> 
>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>> ---
>>  .../bindings/sound/qcom,pm4125-codec.yaml          | 147 +++++++++++++++++++++
>>  .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |  86 ++++++++++++
>>  2 files changed, 233 insertions(+)
>> 
>> 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 0000000000000000000000000000000000000000..1b6ce8d4397b4c1c048899bd2cc4d02318cc46c9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml

[..]

>> +  '#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"
>
> Keep consistent quotes, either ' or "
>
>> +
>> +additionalProperties: false
>
> This has to unevaluatedProperties

Ok for both points, I'll change it.

>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/spmi/spmi.h>
>> +
>> +    spmi {
>> +        #address-cells = <2>;
>> +        #size-cells = <0>;
>> +
>> +        pmic@0 {
>
> pmic {
>
>> +            compatible = "qcom,pm8916", "qcom,spmi-pmic";
>
> Drop, you have warnings here.
>
>> +            reg = <0x0 SPMI_USID>;
>
> Drop

Ok to points above, I'll remove it.

>> +            #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 {
>
> Drop this and next one.

The audio-codec node supposed to have qcom,{rx,tx}-device properties.
If I'll drop it then the example doesn't compile well unless I am missing
something?

For example when I removed soundwire tx node completely and dropped
qcom,tx-device then:

Documentation/devicetree/bindings/sound/qcom,pm4125-codec.example.dtb: audio-codec@f000 (qcom,pm4125-codec): 'qcom,tx-device' is a required property
	from schema $id: http://devicetree.org/schemas/sound/qcom,pm4125-codec.yaml#

Or should it be qcom,tx-device = <&null_placeholder_something> ?
I can't find any examples.

I guess I can drop example from the other file.

>> +        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>;
>> +        };
>> +    };
>> +
>> +    soundwire@a740000 {
>> +        reg = <0x0a740000 0x2000>;
>> +        #address-cells = <2>;
>> +        #size-cells = <0>;
>> +        pm4125_tx: audio-codec@0,3 {
>> +            compatible = "sdw20217010c00";
>> +            reg = <0 3>;
>> +            qcom,tx-port-mapping = <1 1>;
>> +        };
>> +    };
>> +...
>> 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 0000000000000000000000000000000000000000..7241d2ab5dcf4a0d5f25a75acb33a335f93d3b5e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
>> @@ -0,0 +1,86 @@
>> +# 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: |
>
> Drop |

Ack.

>> +  The audio codec IC found on Qualcomm PM4125/PM2250 PMICs.
>> +  It has RX and TX Soundwire slave devices. This bindings is for the
>> +  slave devices.
>
> Last sentence is redundant and makes no sense. Codec has only slave
> devices, so how this can be anything else than for slave devices?

This came from other similar files that describe bindings for child codec nodes
of soundwire nodes. For example from qcom,wcd937x-sdw.yaml.
Should this be rephrased to "This bindings is for the 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
>
> rx and tx are excluding, so this should be here encoded.

Ok, I think I found a way to change it.

>> +
>> +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>;
>> +        };
>> +    };
>> +
>> +    soundwire@a740000 {
>> +        reg = <0x0a740000 0x2000>;
>
> One example is enough, they are the same.

Ok, I'll drop it (see my comment above as well).

Thanks,
Alexey

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add pm4125 audio codec
  2025-06-26  8:48   ` Krzysztof Kozlowski
@ 2025-06-28 16:42     ` Alexey Klimov
  2025-06-29 15:00       ` Dmitry Baryshkov
  2025-06-30  8:18       ` Krzysztof Kozlowski
  0 siblings, 2 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-06-28 16:42 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Srinivas Kandagatla, Liam Girdwood,
	Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla

On Thu Jun 26, 2025 at 9:48 AM BST, Krzysztof Kozlowski wrote:
> On 26/06/2025 01:50, Alexey Klimov wrote:
>> PM4125 has audio codec hardware block. Add pattern for respecive node
>> so the devicetree for those blocks can be validated properly.
>> 
>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>
> Remember to ALWAYS explain the dependencies between patches (merging
> strategy), because this now creates impression is independent patch. It
> is not and should be squashed into previous.

What's the proper way to describe such dependency?

Best regards,
Alexey

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-25 23:50 ` [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver Alexey Klimov
  2025-06-26  6:19   ` Krzysztof Kozlowski
  2025-06-26 11:56   ` Mark Brown
@ 2025-06-28 20:24   ` Srinivas Kandagatla
  2025-07-02  1:43     ` Alexey Klimov
  2 siblings, 1 reply; 25+ messages in thread
From: Srinivas Kandagatla @ 2025-06-28 20:24 UTC (permalink / raw)
  To: Alexey Klimov, Srinivas Kandagatla, Liam Girdwood, Mark Brown,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov

On 6/26/25 12:50 AM, Alexey Klimov wrote:
> The audio codec is found in Qualcomm PM2250/PM4125 PMICs and is used on
> platforms like Qualcomm QCM2290. It has soundwire interface and
> corresponding RX and TX slave devices.
> 
> It has only two input channels: HPH left and right. The line output (LO)
> is linked to HPHL so the hardware has some limitations regarding concurrent
> playback via HPH and LO for instance.
> 
> Not all functionality is implemented and there are some number of
> placeholders. Mostly output functionality like analog and digital mics is
> missing at this point and implemented in a possible minimal way.
> The codec driver also uses WCD MBCH framework. The MBHC functionality is
> also implemented in a minimalistic way to enable IRQs.

Lets try to fill these gaps in next version, I have tested Headset,
Lineout  and MIC on my side with additional changes to this.

> 
> Despite all limitations in its current state the line out and HPH playback
> work.

Thanks Alexey for all the work porting the driver from downstream audio
kernel.

my comments below.

> 
> Cc: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
> ---
>  sound/soc/codecs/Kconfig      |   19 +
>  sound/soc/codecs/Makefile     |    8 +
>  sound/soc/codecs/pm4125-sdw.c |  485 +++++++++++
>  sound/soc/codecs/pm4125.c     | 1848 +++++++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/pm4125.h     |  375 +++++++++
>  5 files changed, 2735 insertions(+)
> 
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 6d7e4725d89cd33647770e4f2c4e81445b8335ce..69b08021d165f83f4a7ca18e476cfc7e2473f490 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -297,6 +297,7 @@ config SND_SOC_ALL_CODECS
>  	imply SND_SOC_WCD937X_SDW
>  	imply SND_SOC_WCD938X_SDW
>  	imply SND_SOC_WCD939X_SDW
> +	imply SND_SOC_PM4125_SDW
>  	imply SND_SOC_LPASS_MACRO_COMMON
>  	imply SND_SOC_LPASS_RX_MACRO
>  	imply SND_SOC_LPASS_TX_MACRO
> @@ -2316,6 +2317,24 @@ config SND_SOC_WCD939X_SDW
>  	  The WCD9390/9395 is a audio codec IC Integrated in
>  	  Qualcomm SoCs like SM8650.
>  
> +config SND_SOC_PM4125
> +	depends on SND_SOC_PM4125_SDW
> +	tristate
> +	depends on SOUNDWIRE || !SOUNDWIRE
> +	select SND_SOC_WCD_CLASSH
> +
> +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_WL1273
>  	tristate
>  
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index a68c3d192a1b6ccec513c6bc447c29be532ea70c..e993cc9803c4da60daf230a8181673b45c06aa5b 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -348,6 +348,8 @@ snd-soc-wcd938x-y := wcd938x.o
>  snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
>  snd-soc-wcd939x-y := wcd939x.o
>  snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
> +snd-soc-pm4125-y := pm4125.o
> +snd-soc-pm4125-sdw-y := pm4125-sdw.o
>  snd-soc-wl1273-y := wl1273.o
>  snd-soc-wm-adsp-y := wm_adsp.o
>  snd-soc-wm0010-y := wm0010.o
> @@ -779,6 +781,12 @@ ifdef CONFIG_SND_SOC_WCD939X_SDW
>  # 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_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_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
> diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d09c4f5fb3b7b7918a1a0c25750046b212f1063f
> --- /dev/null
> +++ b/sound/soc/codecs/pm4125-sdw.c
> @@ -0,0 +1,485 @@
> +// 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)),
> +	WCD_SDW_CH(PM4125_COMP_L, PM4125_COMP_PORT, BIT(0)),
> +	WCD_SDW_CH(PM4125_COMP_R, PM4125_COMP_PORT, BIT(1)),
> +};
> +
> +static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
> +	WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_PORT, BIT(0)),
> +	WCD_SDW_CH(PM4125_DMIC0, PM4125_DMIC_0_3_MBHC_PORT, BIT(0)),
> +	WCD_SDW_CH(PM4125_DMIC1, PM4125_DMIC_0_3_MBHC_PORT, BIT(1)),
> +	WCD_SDW_CH(PM4125_DMIC2, PM4125_DMIC_0_3_MBHC_PORT, BIT(2)),
these does not look correct. there are 2 ADCs .
> +};
> +
> +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)
> +{
> +	if (reg > PM4125_ANA_BASE_ADDR &&
> +	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
> +		return pm4125_reg_access_analog[PM4125_REG(reg)] & WR_REG;
Lets not bring in some additional layer here from downstream, pl use the
registers directly here as we do for other codecs.

> +
> +	if (reg > PM4125_DIG_BASE_ADDR &&
> +	    reg < PM4125_DIGITAL_REGISTERS_MAX_SIZE)
> +		return pm4125_reg_access_digital[PM4125_REG(reg)] & WR_REG;
> +
> +	return false;
> +}
> +
> +static bool pm4125_readable_register(struct device *dev, unsigned int reg)
> +{
> +	if (reg > PM4125_ANA_BASE_ADDR &&
> +	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
> +		return pm4125_reg_access_analog[PM4125_REG(reg)] & RD_REG;
> +
> +	if (reg > PM4125_DIG_BASE_ADDR &&
> +	    reg < PM4125_DIGITAL_REGISTERS_MAX_SIZE)
> +		return pm4125_reg_access_digital[PM4125_REG(reg)] & RD_REG;
> +
> +	return pm4125_rdwr_register(dev, reg);
> +}
> +
> +static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
> +{
> +
> +	if (reg > PM4125_ANA_BASE_ADDR &&
> +	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
> +		if ((pm4125_reg_access_analog[PM4125_REG(reg)] & RD_REG) &&
> +		     !(pm4125_reg_access_analog[PM4125_REG(reg)] & WR_REG))
> +			return true;
> +
> +	if (reg > PM4125_DIG_BASE_ADDR &&
> +	    reg < PM4125_DIGITAL_REGISTERS_MAX_SIZE)
> +		if ((pm4125_reg_access_digital[PM4125_REG(reg)] & RD_REG) &&
> +		     !(pm4125_reg_access_digital[PM4125_REG(reg)] & WR_REG))
> +			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_mark_last_busy(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 start 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);
we do have 100 chars per line, do not split the lines unless required.

> +		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 0000000000000000000000000000000000000000..58435b9c75865b06b344c6d79800aa9b4bac3abd
> --- /dev/null
> +++ b/sound/soc/codecs/pm4125.c
> @@ -0,0 +1,1848 @@
> +// 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_params.h>
> +#include <sound/pcm.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/soc.h>
> +#include <sound/tlv.h>
> +
> +#include "wcd-mbhc-v2.h"
> +#include "pm4125.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,
> +};
> +
> +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 regulator_bulk_data supplies[PM4125_MAX_BULK_SUPPLY];
> +	struct regulator *buck_supply;

unused.

> +	struct snd_soc_jack *jack;
> +	unsigned long status_mask;
> +	s32 micb_ref[PM4125_MAX_MICBIAS];
> +	s32 pullup_ref[PM4125_MAX_MICBIAS];
> +	u32 hph_mode;
> +	int ear_rx_path;
> +	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 rx_clk_cnt;
> +	atomic_t ana_clk_count;
unused
> +};
> +
> +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;
> +
> +	if (data)
> +		pm4125 = (struct pm4125_priv *)data;
> +	else
> +		return IRQ_HANDLED;
This will result in interrupt storm, as you are not clearning the source.

> +
> +	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 const 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,
> +	.irq_drv_data = NULL,
> +};
> +
> +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, 0x03, 0x00);
pl use defines instead of using magic values.
applies to most part of the driver.
also pl take a look at
snd_soc_component_read/write_field() functions which are handy
> +
> +	/* Enable surge protection */
> +	regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, 0xC0, 0xC0);
> +
> +	/* Disable mic bias pull down */
> +	regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01, 0x00);
> +}
> +
> +static int pm4125_global_mbias_disable(struct snd_soc_component *component)
> +{
> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
> +				      0x10, 0x00);
> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
> +				      0x20, 0x00);
> +	return 0;
> +}
> +
> +static int pm4125_global_mbias_enable(struct snd_soc_component *component)
> +{
> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
> +				      0x20, 0x20);
> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
> +				      0x10, 0x10);
> +	usleep_range(1000, 1100);
> +	return 0;
> +}
> +
> +static int pm4125_rx_clk_enable(struct snd_soc_component *component)

looks like the rxclk can be converted into a proper dapm supply widget.

> +{
> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	if (atomic_read(&pm4125->rx_clk_cnt))
> +		return 0;
> +
> +	snd_soc_component_update_bits(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +				      0x10, 0x10);
> +	snd_soc_component_update_bits(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +				      0x20, 0x20);
> +	usleep_range(5000, 5100);
> +
> +	pm4125_global_mbias_enable(component);
> +
> +	snd_soc_component_update_bits(component, PM4125_ANA_HPHPA_FSM_CLK,
> +				      0x7F, 0x11);
> +	snd_soc_component_update_bits(component, PM4125_ANA_HPHPA_FSM_CLK,
> +				      0x80, 0x80);
> +	snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL,
> +				      0x07, 0x06);
> +	snd_soc_component_update_bits(component, PM4125_ANA_NCP_EN,
> +				      0x01, 0x01);
> +	usleep_range(500, 510);
> +
> +	atomic_inc(&pm4125->rx_clk_cnt);
> +
> +	return 0;
> +}
> +
> +static int pm4125_rx_clk_disable(struct snd_soc_component *component)
> +{
> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	if (!atomic_read(&pm4125->rx_clk_cnt)) {
> +		dev_err(component->dev, "clk already disabled\n");
> +		return 0;
> +	}
> +
> +	atomic_dec(&pm4125->rx_clk_cnt);
> +
> +	snd_soc_component_update_bits(component,
> +				      PM4125_ANA_HPHPA_FSM_CLK,
> +				      0x80, 0x00);
> +	snd_soc_component_update_bits(component,
> +				      PM4125_ANA_HPHPA_FSM_CLK,
> +				      0x7F, 0x00);
> +	snd_soc_component_update_bits(component,
> +				      PM4125_ANA_NCP_EN,
> +				      0x01, 0x00);
> +	snd_soc_component_update_bits(component,
> +				      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +				      0x20, 0x00);
> +	snd_soc_component_update_bits(component,
> +				      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +				      0x10, 0x00);
> +	pm4125_global_mbias_disable(component);
> +
> +	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:
> +		pm4125_rx_clk_enable(component);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_ANA_HPHPA_CNP_CTL_1,
> +					      0x02, 0x02);
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_SWR_HPHPA_HD2,
> +					      0x38, 0x38);
> +
> +		set_bit(HPH_COMP_DELAY, &pm4125->status_mask);
> +		break;
> +	case SND_SOC_DAPM_POST_PMU:
> +
drop space.
> +		if (pm4125->comp1_enable) {
> +			snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x02, 0x02);
> +
> +			if (pm4125->comp2_enable)
> +				snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x01, 0x01);
> +			/*
> +			 * 5ms sleep is required after COMP is enabled as per
> +			 * HW requirement
> +			 */
> +			if (test_bit(HPH_COMP_DELAY, &pm4125->status_mask)) {
> +				usleep_range(5000, 5100);
> +				clear_bit(HPH_COMP_DELAY, &pm4125->status_mask);
> +			}
> +		} else {
> +			snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x02, 0x00);
> +		}
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX0_CTL,
> +					      0x80, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
> +					      0x04, 0x04);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +					      0x01, 0x01);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +					      0x01, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
> +					      0x04, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX0_CTL,
> +					      0x80, 0x80);
> +		if (pm4125->comp1_enable)
> +			snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x02, 0x00);
> +		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);
this can fit in 100 chars.

> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_PRE_PMU:
> +		pm4125_rx_clk_enable(component);
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_ANA_HPHPA_CNP_CTL_1,
> +					      0x02, 0x02);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_SWR_HPHPA_HD2,
> +					      0x07, 0x07);
> +
> +		set_bit(HPH_COMP_DELAY, &pm4125->status_mask);
> +		break;
> +	case SND_SOC_DAPM_POST_PMU:
> +		if (pm4125->comp2_enable) {
> +			snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x01, 0x01);
> +
> +			if (pm4125->comp1_enable)
> +				snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x02, 0x02);
> +			/*
> +			 * 5ms sleep is required after COMP is enabled
> +			 * as per HW requirement
> +			 */
> +			if (test_bit(HPH_COMP_DELAY, &pm4125->status_mask)) {
> +				usleep_range(5000, 5100);
> +				clear_bit(HPH_COMP_DELAY, &pm4125->status_mask);
> +			}
> +		} else {
> +			snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x01, 0x00);
> +		}
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX1_CTL,
> +					      0x80, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
> +					      0x08, 0x08);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +					      0x02, 0x02);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +					      0x02, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
> +					      0x08, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX1_CTL,
> +					      0x80, 0x80);
> +
> +		if (pm4125->comp2_enable)
> +			snd_soc_component_update_bits(component,
> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
> +						0x01, 0x00);
> +		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:
> +		pm4125_rx_clk_enable(component);
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX0_CTL,
> +					      0x80, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +					      0x01, 0x01);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
> +					      0x04, 0x04);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
> +					      0x01, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
> +					      0x04, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_RX0_CTL,
> +					      0x80, 0x80);
> +		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);
> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_PRE_PMU:
> +		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		usleep_range(200, 210);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL1,
> +					      0x03, 0x03);
> +		break;
> +	case SND_SOC_DAPM_POST_PMU:
> +		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
> +			usleep_range(5000, 5100);
> +			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		}
> +
> +		enable_irq(pm4125->hphr_pdm_wd_int);
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		disable_irq_nosync(pm4125->hphr_pdm_wd_int);
> +		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
> +			usleep_range(5000, 5100);
> +			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		}
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL1,
> +					      0x03, 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);
> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_PRE_PMU:
> +		usleep_range(200, 210);
> +		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
Am really not following the logic here, we set the bit here and check
the bit in POST_PMU and then we set BIT in PRE_PMD and clear it int
POST_PMD.

So this bit will be set in all the cases.

why do we even need this bit to be set, can we not use the dealy by default?

> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
> +					      0x03, 0x03);
> +		break;
> +	case SND_SOC_DAPM_POST_PMU:
> +		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
> +			usleep_range(5000, 5100);
> +			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		}
> +
> +		enable_irq(pm4125->hphl_pdm_wd_int);
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
> +		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
> +			usleep_range(5000, 5100);
> +			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
> +		}
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
> +					      0x03, 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);
> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	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,
> +					      0x40, 0x40);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
> +					      0x03, 0x03);
> +		break;
> +	case SND_SOC_DAPM_POST_PMU:
> +		usleep_range(5000, 5010);
> +
> +		snd_soc_component_update_bits(component,
> +					      PM4125_ANA_COMBOPA_CTL_4,
> +					      0x0F, 0x04);
> +		enable_irq(pm4125->hphl_pdm_wd_int);
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		usleep_range(2000, 2010);
> +
Empty lining is really not consistent across the code, either use it
consistently or not use it at all.

> +		snd_soc_component_update_bits(component,
> +					      PM4125_ANA_COMBOPA_CTL,
> +					      0x40, 0x00);
> +		usleep_range(5000, 5100);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
> +					      0x03, 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);
> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
> +
> +	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,
> +					      0x40, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
> +					      0x03, 0x03);
> +		break;
> +	case SND_SOC_DAPM_POST_PMU:
> +		usleep_range(5000, 5010);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_ANA_COMBOPA_CTL_4,
> +					      0x0F, 0x04);
> +
> +		enable_irq(pm4125->hphl_pdm_wd_int);
> +		break;
> +	case SND_SOC_DAPM_PRE_PMD:
> +		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		usleep_range(5000, 5010);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
> +					      0x03, 0x00);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pm4125_enable_rx1(struct snd_soc_dapm_widget *w,
> +			     struct snd_kcontrol *kcontrol, int event)
> +{
> +	struct snd_soc_component *component =
> +					snd_soc_dapm_to_component(w->dapm);
> +
> +	if (event == SND_SOC_DAPM_POST_PMD)
> +		pm4125_rx_clk_disable(component);

if we convert the rx clk into dapm, then things will be inplace
automatically and you do not do this kinda handling.

> +
> +	return 0;
> +}
> +
> +static int pm4125_enable_rx2(struct snd_soc_dapm_widget *w,
> +			     struct snd_kcontrol *kcontrol, int event)
> +{
> +	struct snd_soc_component *component =
> +					snd_soc_dapm_to_component(w->dapm);
> +
> +	if (event == SND_SOC_DAPM_POST_PMD)
> +		pm4125_rx_clk_disable(component);
> +
> +	return 0;
> +}

This function is duplicate of pm4125_enable_rx1()...

> +
> +static int pm4125_get_micb_vout_ctl_val(u32 micb_mv)
> +{
> +	if (micb_mv < 1600 || micb_mv > 2850) {
> +		pr_err("Unsupported micbias voltage (%u mV)\n", 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)
> +{
Lets add the code needed for ADC in next spin.
> +	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;
> +
> +	switch (w->shift) {
> +	case 0:
> +	case 1:
Can we use proper names here, instead of 1 and 0, can we use
PM4125_DMIC0, PM4125_DMIC1 ?

> +		dmic_clk_reg = PM4125_DIG_SWR_CDC_DMIC1_CTL;
Also why can not we pass this as reg to the widget? then you can avoid
all this switch caseing.

> +		break;
> +	default:
> +		dev_err(component->dev, "Invalid DMIC selection\n");
> +		return -EINVAL;
> +	}
> +
> +	switch (event) {
> +	case SND_SOC_DAPM_PRE_PMU:
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_AMIC_CTL,
> +					      0x02, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      dmic_clk_reg,
> +					      0x08, 0x08);
> +		break;
> +	case SND_SOC_DAPM_POST_PMD:
> +		snd_soc_component_update_bits(component,
> +					      dmic_clk_reg,
> +					      0x08, 0x00);
> +		snd_soc_component_update_bits(component,
> +					      PM4125_DIG_SWR_CDC_AMIC_CTL,
> +					      0x02, 0x02);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pm4125_micbias_control(struct snd_soc_component *component,
> +				  int micb_num, int req, bool is_dapm)
> +{
Lets implement this in v2.
> +	return 0;
> +}
> +
> +static int __pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w,
> +					 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_ENABLE, true);
MICBIAS 3 needs internal LDO pull up, as we can not connect it to
vairable voltage.

> +		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_DISABLE, true);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w,
> +				       struct snd_kcontrol *kcontrol, int event)
> +{
> +	return __pm4125_codec_enable_micbias(w, event);
Why do we need this boiler plate, same comment applies to other parts of
the code too.

> +}
> +
> +static int __pm4125_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
> +						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_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
> +					      struct snd_kcontrol *kcontrol,
> +					      int event)
> +{
> +	return __pm4125_codec_enable_micbias_pullup(w, event);
> +}
> +
> +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 const struct snd_kcontrol_new hph_type_detect_controls[] = {
> +	SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, NULL, NULL),
> +};
> +
> +static const struct snd_kcontrol_new impedance_detect_controls[] = {
> +	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, NULL, NULL),
> +	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, NULL, NULL),

What are these supposed to read?

> +};
> +
> +static void pm4125_mbhc_mbhc_bias_control(struct snd_soc_component *component,
> +					  bool enable)
> +{
> +	snd_soc_component_update_bits(component, PM4125_ANA_MBHC_ELECT, 0x01,
> +				      enable ? 0x01 : 0x00);
> +}
> +
> +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,
> +					0xfc, vth << 2);
> +	}
> +}
> +
> +static const struct wcd_mbhc_cb mbhc_cb = {
> +	.clk_setup = NULL,
> +	.mbhc_bias = pm4125_mbhc_mbhc_bias_control,
> +	.set_btn_thr = pm4125_mbhc_program_btn_thr,
> +	.micbias_enable_status = NULL,
> +	.hph_pull_up_control_v2 = NULL,
> +	.mbhc_micbias_control = NULL,
> +	.mbhc_micb_ramp_control = NULL,
> +	.mbhc_micb_ctrl_thr_mic = NULL,
> +	.compute_impedance = NULL,
> +	.mbhc_gnd_det_ctrl = NULL,
> +	.hph_pull_down_ctrl = NULL,
> +	.mbhc_moisture_config = NULL,
> +	.mbhc_get_moisture_status = NULL,
> +	.mbhc_moisture_polling_ctrl = NULL,
> +	.mbhc_moisture_detect_en = NULL,
Either we add mbhc or not, having these dummy functions is not really
helping.

> +};
> +
> +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);
> +
> +	snd_soc_add_component_controls(component, impedance_detect_controls,
> +				       ARRAY_SIZE(impedance_detect_controls));
> +	snd_soc_add_component_controls(component, hph_type_detect_controls,
> +				       ARRAY_SIZE(hph_type_detect_controls));
> +	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", SND_SOC_NOPM, 0, 1, 0,
> +		       pm4125_get_compander, pm4125_set_compander),
> +	SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 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("LO Switch", PM4125_LO, 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),
> +	SOC_SINGLE_EXT("DMIC0 Switch", PM4125_DMIC0, 1, 1, 0,
> +		       pm4125_get_swr_port, pm4125_set_swr_port),
> +	SOC_SINGLE_EXT("DMIC1 Switch", PM4125_DMIC1, 1, 1, 0,
> +		       pm4125_get_swr_port, pm4125_set_swr_port),
> +	SOC_SINGLE_EXT("MBHC Switch", PM4125_MBHC, 1, 1, 0,
> +		       pm4125_get_swr_port, pm4125_set_swr_port),
> +	SOC_SINGLE_EXT("DMIC2 Switch", PM4125_DMIC2, 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 adc3_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_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0,
> +			     adc1_switch, ARRAY_SIZE(adc1_switch),
> +			     NULL, SND_SOC_DAPM_PRE_PMU |
> +			     SND_SOC_DAPM_POST_PMD),
> +	SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 1, 0,
> +			     adc2_switch, ARRAY_SIZE(adc2_switch),
> +			     NULL, SND_SOC_DAPM_PRE_PMU |
> +			     SND_SOC_DAPM_POST_PMD),
> +
> +	/* 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_PRE_PMD | 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_PRE_PMD | 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_PMU |
> +			   SND_SOC_DAPM_PRE_PMD | 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_PMU |
> +			   SND_SOC_DAPM_PRE_PMD | 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_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0,
> +			     pm4125_enable_rx1, SND_SOC_DAPM_PRE_PMU |
> +			     SND_SOC_DAPM_POST_PMD),
> +	SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0,
> +			     pm4125_enable_rx2, SND_SOC_DAPM_PRE_PMU |
> +			     SND_SOC_DAPM_POST_PMD),
> +
> +	/* 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, SND_SOC_NOPM, 0, 0,
> +			   pm4125_codec_enable_dmic,
> +			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
> +	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
> +			   pm4125_codec_enable_dmic,
> +			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
> +
> +	/* TX mixer widgets */
> +	SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0,
> +			     0, dmic1_switch, ARRAY_SIZE(dmic1_switch),
> +			     NULL, SND_SOC_DAPM_PRE_PMU |
> +			     SND_SOC_DAPM_POST_PMD),
> +	SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 1,
> +			     0, dmic2_switch, ARRAY_SIZE(dmic2_switch),
> +			     NULL, SND_SOC_DAPM_PRE_PMU |
> +			     SND_SOC_DAPM_POST_PMD),
> +
> +	/* 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" },
> +	{ "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" },
> +	{ "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" },
> +
> +	{ "RDAC3", NULL, "RX1" },
> +	{ "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 pm4125_priv *pm4125)
> +{
> +	int vout_ctl;
> +
> +	/* Set micbias voltage */
> +	vout_ctl = pm4125_get_micb_vout_ctl_val(pm4125->micb1_mv);
> +	if (vout_ctl < 0)
> +		return -EINVAL;
> +
> +	regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
> +			   0xF8, vout_ctl << 3);
> +	return 0;
> +}
> +
> +static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
> +{
> +	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;
> +	}
> +
> +	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 interrupt */
> +	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 bool pm4125_swap_gnd_mic(struct snd_soc_component *component)
> +{
> +	return true;

why true all the time? Isn't this suppose to control a mux or a gpio?


> +}
> +
> +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 = "WCD 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 = "WCD 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);
> +	int ret;
> +
> +	/* Give the SDW 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 slave with matching of node\n");
> +		return -EINVAL;
> +	}
> +
> +	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");
> +		return -EINVAL;
> +	}
> +
> +	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");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * As TX is the main CSR reg interface, which should not be suspended first.
> +	 * expicilty add the dependency link
> +	 */
> +	if (!device_link_add(pm4125->rxdev, pm4125->txdev,
> +			     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
> +		dev_err(dev, "Could not devlink TX and RX\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!device_link_add(dev, pm4125->txdev,
> +			     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
> +		dev_err(dev, "Could not devlink WCD and TX\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!device_link_add(dev, pm4125->rxdev,
> +			     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) {
> +		dev_err(dev, "Could not devlink WCD and RX\n");
> +		return -EINVAL;
> +	}
> +
> +	pm4125->regmap = dev_get_regmap(&pm4125->tx_sdw_dev->dev, NULL);
> +	if (!pm4125->regmap) {
> +		dev_err(dev, "could not get TX device regmap\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = pm4125_irq_init(pm4125, dev);
> +	if (ret) {
> +		dev_err(dev, "IRQ init failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
> +	pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
> +
> +	ret = pm4125_set_micbias_data(pm4125);
> +	if (ret < 0) {
> +		dev_err(dev, "Bad micbias pdata\n");
> +		return ret;
> +	}
> +
> +	ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
> +					 pm4125_dais, ARRAY_SIZE(pm4125_dais));
> +	if (ret)
> +		dev_err(dev, "Codec registration failed\n");
> +
> +	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) {
> +		dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n");
> +		return -ENODEV;
> +	}
> +	of_node_get(pm4125->rxnode);
> +	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) {
> +		dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n");
> +			return -ENODEV;
> +	}
> +	of_node_get(pm4125->txnode);
> +	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);
> +
> +	cfg = &pm4125->mbhc_cfg;
> +	cfg->swap_gnd_mic = pm4125_swap_gnd_mic;
> +
> +	pm4125->supplies[0].supply = "vdd-io";
> +	pm4125->supplies[1].supply = "vdd-cp";
> +	pm4125->supplies[2].supply = "vdd-mic-bias";
> +	pm4125->supplies[3].supply = "vdd-pa-vpos";
> +
> +	ret = devm_regulator_bulk_get(dev, PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to get supplies\n");
> +
> +	ret = regulator_bulk_enable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	if (ret) {
> +		regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +		return dev_err_probe(dev, ret, "Failed to enable supplies\n");
> +	}
> +
> +	pm4125_dt_parse_micbias_info(dev, pm4125);
> +
> +	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;
> +
> +	pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!pm4125->spmi_regmap)
> +		return -ENXIO;
> +
> +	pm4125_reset(pm4125);
> +
> +	wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
> +
> +	ret = pm4125_add_slave_components(pm4125, dev, &match);
> +	if (ret)
> +		goto err_disable_regulators;
> +
> +	ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
> +	if (ret)
> +		goto err_disable_regulators;
> +
> +	pm_runtime_set_autosuspend_delay(dev, 1000);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_idle(dev);
> +
> +	return 0;
> +
> +err_disable_regulators:
> +	regulator_bulk_disable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +
> +	return ret;
> +}
> +
> +static void pm4125_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
> +
> +	component_master_del(&pdev->dev, &pm4125_comp_ops);
> +
> +	pm_runtime_disable(dev);
> +	pm_runtime_set_suspended(dev);
> +	pm_runtime_dont_use_autosuspend(dev);
> +
> +	regulator_bulk_disable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +	regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
> +}
> +
> +#if defined(CONFIG_OF)
> +static const struct of_device_id pm4125_of_match[] = {
> +	{ .compatible = "qcom,pm4125-codec" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
> +#endif
> +
> +static struct platform_driver pm4125_codec_driver = {
> +	.probe = pm4125_probe,
> +	.remove = pm4125_remove,
> +	.driver = {
> +		.name = "pm4125_codec",
> +		.of_match_table = of_match_ptr(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 0000000000000000000000000000000000000000..2c5e8218202d92a0adc493413368991a406471b0
> --- /dev/null
> +++ b/sound/soc/codecs/pm4125.h
> @@ -0,0 +1,375 @@
> +/* 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_REG(reg)	((reg > PM4125_DIG_BASE_ADDR) ? \
> +			 (reg - PM4125_DIG_BASE_ADDR) : \
> +			 (reg - PM4125_ANA_BASE_ADDR))
> +
> +enum {
> +	REG_NO_ACCESS,
> +	RD_REG,
> +	WR_REG,
> +	RD_WR_REG
> +};
> +
Both PM4125_REG(), and RD/WR_REG does not make sense, please use the
registers directly in the regmap config callbacks.

...
> +#define PM4125_MAX_MICBIAS			3
> +#define PM4125_MAX_BULK_SUPPLY			4
> +#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_PORT = 1,
> +	PM4125_DMIC_0_3_MBHC_PORT,
> +	PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_0_3_MBHC_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
> +int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
Should this be static inline, if not you will endup with multiple
defination of the function.
same for other stubs too.

> +		    struct snd_pcm_substream *substream,
> +		    struct snd_soc_dai *dai)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
> +			      struct snd_soc_dai *dai,
> +			      void *stream, int direction)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +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,
> +	PM4125_ADC3,
> +	PM4125_DMIC0,
> +	PM4125_DMIC1,
> +	PM4125_MBHC,
> +	PM4125_DMIC2,
> +	PM4125_DMIC3,
> +	PM4125_DMIC4,
> +	PM4125_DMIC5,
> +	PM4125_DMIC6,
do we really have so many channels on TX, AFAIU, its only 2 channels and
two lanes.
> +};
> +
> +enum pm4125_rx_sdw_channels {
> +	PM4125_HPH_L,
> +	PM4125_HPH_R,
> +	PM4125_CLSH,
> +	PM4125_COMP_L,
> +	PM4125_COMP_R,
> +	PM4125_LO,
> +	PM4125_DSD_R,
> +	PM4125_DSD_L,
Do we have so many channes? AFAIU, this is only 4 channels and 1 lane.

> +};
> +
> +#endif /* _PM4125_REGISTERS_H */
> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-28 16:41     ` Alexey Klimov
@ 2025-06-29 14:59       ` Dmitry Baryshkov
  2025-06-30  8:21       ` Krzysztof Kozlowski
  1 sibling, 0 replies; 25+ messages in thread
From: Dmitry Baryshkov @ 2025-06-29 14:59 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Krzysztof Kozlowski, Srinivas Kandagatla, Liam Girdwood,
	Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Stephen Boyd, Lee Jones, Jaroslav Kysela, Takashi Iwai,
	linux-arm-msm, linux-sound, devicetree, linux-kernel,
	Srinivas Kandagatla

On Sat, Jun 28, 2025 at 05:41:31PM +0100, Alexey Klimov wrote:
> On Thu Jun 26, 2025 at 7:13 AM BST, Krzysztof Kozlowski wrote:
> > On Thu, Jun 26, 2025 at 12:50:29AM +0100, Alexey Klimov wrote:
> >> The audio codec IC is found on Qualcomm PM4125/PM2250 PMIC.
> >> It has TX and RX soundwire slave devices hence two files
> >> are added.
> >> 
> >> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
> >> ---
> >>  .../bindings/sound/qcom,pm4125-codec.yaml          | 147 +++++++++++++++++++++
> >>  .../devicetree/bindings/sound/qcom,pm4125-sdw.yaml |  86 ++++++++++++
> >>  2 files changed, 233 insertions(+)
> >> 
> >> 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 0000000000000000000000000000000000000000..1b6ce8d4397b4c1c048899bd2cc4d02318cc46c9
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-codec.yaml
> >> +
> >> +    /* ... */
> >> +
> >> +    soundwire@a610000 {
> >
> > Drop this and next one.
> 
> The audio-codec node supposed to have qcom,{rx,tx}-device properties.
> If I'll drop it then the example doesn't compile well unless I am missing
> something?

Examples are compiled as plugins, so you can reference non-existing
handles. Keep the property in the codec and drop the soundwire devices.


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add pm4125 audio codec
  2025-06-28 16:42     ` Alexey Klimov
@ 2025-06-29 15:00       ` Dmitry Baryshkov
  2025-06-30  8:18       ` Krzysztof Kozlowski
  1 sibling, 0 replies; 25+ messages in thread
From: Dmitry Baryshkov @ 2025-06-29 15:00 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Krzysztof Kozlowski, Srinivas Kandagatla, Liam Girdwood,
	Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Stephen Boyd, Lee Jones, Jaroslav Kysela, Takashi Iwai,
	linux-arm-msm, linux-sound, devicetree, linux-kernel,
	Srinivas Kandagatla

On Sat, Jun 28, 2025 at 05:42:52PM +0100, Alexey Klimov wrote:
> On Thu Jun 26, 2025 at 9:48 AM BST, Krzysztof Kozlowski wrote:
> > On 26/06/2025 01:50, Alexey Klimov wrote:
> >> PM4125 has audio codec hardware block. Add pattern for respecive node
> >> so the devicetree for those blocks can be validated properly.
> >> 
> >> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
> >
> > Remember to ALWAYS explain the dependencies between patches (merging
> > strategy), because this now creates impression is independent patch. It
> > is not and should be squashed into previous.
> 
> What's the proper way to describe such dependency?

Text in the cover letter.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add pm4125 audio codec
  2025-06-28 16:42     ` Alexey Klimov
  2025-06-29 15:00       ` Dmitry Baryshkov
@ 2025-06-30  8:18       ` Krzysztof Kozlowski
  1 sibling, 0 replies; 25+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-30  8:18 UTC (permalink / raw)
  To: Alexey Klimov, Srinivas Kandagatla, Liam Girdwood, Mark Brown,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov,
	Srinivas Kandagatla

On 28/06/2025 18:42, Alexey Klimov wrote:
> On Thu Jun 26, 2025 at 9:48 AM BST, Krzysztof Kozlowski wrote:
>> On 26/06/2025 01:50, Alexey Klimov wrote:
>>> PM4125 has audio codec hardware block. Add pattern for respecive node
>>> so the devicetree for those blocks can be validated properly.
>>>
>>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>>
>> Remember to ALWAYS explain the dependencies between patches (merging
>> strategy), because this now creates impression is independent patch. It
>> is not and should be squashed into previous.
> 
> What's the proper way to describe such dependency?

The best changelog. Acceptable is also cover letter, although some
people skip cover letters.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-28 16:41     ` Alexey Klimov
  2025-06-29 14:59       ` Dmitry Baryshkov
@ 2025-06-30  8:21       ` Krzysztof Kozlowski
  2025-07-01 23:30         ` Alexey Klimov
  1 sibling, 1 reply; 25+ messages in thread
From: Krzysztof Kozlowski @ 2025-06-30  8:21 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On 28/06/2025 18:41, Alexey Klimov wrote:
> 
>>> +            #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 {
>>
>> Drop this and next one.
> 
> The audio-codec node supposed to have qcom,{rx,tx}-device properties.
> If I'll drop it then the example doesn't compile well unless I am missing
> something?

What did you drop and what did I ask to drop?

> 
> For example when I removed soundwire tx node completely and dropped
> qcom,tx-device then:

I did not ask to drop qcom,tx-device.

...

> 
>>> +  The audio codec IC found on Qualcomm PM4125/PM2250 PMICs.
>>> +  It has RX and TX Soundwire slave devices. This bindings is for the
>>> +  slave devices.
>>
>> Last sentence is redundant and makes no sense. Codec has only slave
>> devices, so how this can be anything else than for slave devices?
> 
> This came from other similar files that describe bindings for child codec nodes
> of soundwire nodes. For example from qcom,wcd937x-sdw.yaml.
> Should this be rephrased to "This bindings is for the soundwire slave devices." ?

You just pasted the same, so I don't get how you want to rephrase into
the same sentence.

> 
Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-26 11:56   ` Mark Brown
@ 2025-07-01 19:35     ` Alexey Klimov
  2025-07-01 21:04       ` Mark Brown
  0 siblings, 1 reply; 25+ messages in thread
From: Alexey Klimov @ 2025-07-01 19:35 UTC (permalink / raw)
  To: Mark Brown
  Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Thu Jun 26, 2025 at 12:56 PM BST, Mark Brown wrote:
> On Thu, Jun 26, 2025 at 12:50:31AM +0100, Alexey Klimov wrote:
>
>> --- a/sound/soc/codecs/Kconfig
>> +++ b/sound/soc/codecs/Kconfig
>> @@ -297,6 +297,7 @@ config SND_SOC_ALL_CODECS
>>  	imply SND_SOC_WCD937X_SDW
>>  	imply SND_SOC_WCD938X_SDW
>>  	imply SND_SOC_WCD939X_SDW
>> +	imply SND_SOC_PM4125_SDW
>>  	imply SND_SOC_LPASS_MACRO_COMMON
>>  	imply SND_SOC_LPASS_RX_MACRO
>>  	imply SND_SOC_LPASS_TX_MACRO
>
> Please keep this file sorted, there's obviously been some things missed
> but please don't make it worse.
>
>> +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
>
> Other drivers sort this out in Kconfig, do as they do.

My bad, thanks for pointing it out. I'll change that.

>> +static int pm4125_micbias_control(struct snd_soc_component *component,
>> +				  int micb_num, int req, bool is_dapm)
>> +{
>> +	return 0;
>> +}
>
> Why have this empty function which is only called from within the
> driver?  At best it's making the callers look like they do something.

I tried to make a minimal working version that we're going to
update with more patches during next submission.

Right now there seems to be at least two approaches:
-- pull in everything and send it in one go. Srini said that it will
be much more difficult to review due to the volume of code;
-- provide few patches that iteratively update the initial one and
add more functionality. The similar way when wcd937x was posted.
I counted there 5 patches for wcd937x. We probably can do that with
this audio-codec. In that case I need to figure out the right way
to split it.

My main issue was MBHC part -- it is needed to avoid IRQ storm
from pdm watchdog interrupts but MBHC requires more things to be
added for its support, that's why there are some empty/placeholder
functions.

What do you think should be the right strategy here?

In theory I can remove MBHC and make much smaller driver but
it will suffer from IRQ storms with some kernel WARNINGs generated.
Or maybe I should remove watchdog interrupts from it as well.

>> +static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
>> +{
>> +	return IRQ_HANDLED;
>> +}
>
> Why bother regisering for the interrupt at all if you're just going to
> ignore it?

This approach seems to be inherited from older wcd-family codec and
wcd939x.c (wcd939x_wd_handle_irq) provides this comment that I can copy
and adjust here like this:

/*
 * 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.
 */


>> +#if defined(CONFIG_OF)
>> +static const struct of_device_id pm4125_of_match[] = {
>> +	{ .compatible = "qcom,pm4125-codec" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
>> +#endif
>
> Why does this compatible exist?  If the driver is instantiated from a
> as a Linux software contruct it shouldn't appear in the DT.

Could you please elaborate a bit more? Should it be instantiated
as an MFD device or platform device?

As far as I understood we need references to soundwire child nodes
of the codec (which are in DT) hence this one is described in the DT.


>> +const u8 pm4125_reg_access_digital[
>> +	PM4125_REG(PM4125_DIGITAL_REGISTERS_MAX_SIZE)] = {
>> +		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID0)] = RD_REG,
>> +		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID1)] = RD_REG,
>> +		[PM4125_REG(PM4125_DIG_SWR_CHIP_ID2)] = RD_REG,
>
> Data tables like this shouldn't be in headers, they should be in C
> files.  At worst you might end up with duplicate copies in the object
> code.

Thanks, I pull in a change/patch that fixes-reworks this.

Thank you,
Alexey Klimov


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-07-01 19:35     ` Alexey Klimov
@ 2025-07-01 21:04       ` Mark Brown
  2025-07-10 14:45         ` Alexey Klimov
  0 siblings, 1 reply; 25+ messages in thread
From: Mark Brown @ 2025-07-01 21:04 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

[-- Attachment #1: Type: text/plain, Size: 1308 bytes --]

On Tue, Jul 01, 2025 at 08:35:42PM +0100, Alexey Klimov wrote:
> On Thu Jun 26, 2025 at 12:56 PM BST, Mark Brown wrote:
> > On Thu, Jun 26, 2025 at 12:50:31AM +0100, Alexey Klimov wrote:

> >> +static int pm4125_micbias_control(struct snd_soc_component *component,
> >> +				  int micb_num, int req, bool is_dapm)
> >> +{
> >> +	return 0;
> >> +}

> > Why have this empty function which is only called from within the
> > driver?  At best it's making the callers look like they do something.

> I tried to make a minimal working version that we're going to
> update with more patches during next submission.

Add the callers when you need them, right now this is just noise.
Nobody can tell if the callers make sense since the function does
nothing.

> >> +#if defined(CONFIG_OF)
> >> +static const struct of_device_id pm4125_of_match[] = {
> >> +	{ .compatible = "qcom,pm4125-codec" },
> >> +	{ }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
> >> +#endif

> > Why does this compatible exist?  If the driver is instantiated from a
> > as a Linux software contruct it shouldn't appear in the DT.

> Could you please elaborate a bit more? Should it be instantiated
> as an MFD device or platform device?

Yes, if it's the child of a MFD then it shouldn't need to be described
separately in the DT.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-06-30  8:21       ` Krzysztof Kozlowski
@ 2025-07-01 23:30         ` Alexey Klimov
  2025-07-02  6:22           ` Krzysztof Kozlowski
  0 siblings, 1 reply; 25+ messages in thread
From: Alexey Klimov @ 2025-07-01 23:30 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Mon Jun 30, 2025 at 9:21 AM BST, Krzysztof Kozlowski wrote:
> On 28/06/2025 18:41, Alexey Klimov wrote:
>> 
>>>> +            #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 {
>>>
>>> Drop this and next one.
>> 
>> The audio-codec node supposed to have qcom,{rx,tx}-device properties.
>> If I'll drop it then the example doesn't compile well unless I am missing
>> something?
>
> What did you drop and what did I ask to drop?
>
>> 
>> For example when I removed soundwire tx node completely and dropped
>> qcom,tx-device then:
>
> I did not ask to drop qcom,tx-device.

Dmitry already explained. Ok.

> ...
>
>> 
>>>> +  The audio codec IC found on Qualcomm PM4125/PM2250 PMICs.
>>>> +  It has RX and TX Soundwire slave devices. This bindings is for the
>>>> +  slave devices.
>>>
>>> Last sentence is redundant and makes no sense. Codec has only slave
>>> devices, so how this can be anything else than for slave devices?
>> 
>> This came from other similar files that describe bindings for child codec nodes
>> of soundwire nodes. For example from qcom,wcd937x-sdw.yaml.
>> Should this be rephrased to "This bindings is for the soundwire slave devices." ?
>
> You just pasted the same, so I don't get how you want to rephrase into
> the same sentence.

Not really.
Original sentence: "This bindings is for the slave devices."
Sentence from my email: "This bindings is for the soundwire slave devices."

The difference is 1 word.
If it doesn't work, then maybe any suggestions?

Maybe "This bindings is for audio codec node that must be a child node of the
associated soundwire master node."?

Best regards,
Alexey

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-28 20:24   ` Srinivas Kandagatla
@ 2025-07-02  1:43     ` Alexey Klimov
  2025-07-03 12:50       ` Srinivas Kandagatla
  0 siblings, 1 reply; 25+ messages in thread
From: Alexey Klimov @ 2025-07-02  1:43 UTC (permalink / raw)
  To: Srinivas Kandagatla, Srinivas Kandagatla, Liam Girdwood,
	Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov

On Sat Jun 28, 2025 at 9:24 PM BST, Srinivas Kandagatla wrote:
> On 6/26/25 12:50 AM, Alexey Klimov wrote:
>> The audio codec is found in Qualcomm PM2250/PM4125 PMICs and is used on
>> platforms like Qualcomm QCM2290. It has soundwire interface and
>> corresponding RX and TX slave devices.
>> 
>> It has only two input channels: HPH left and right. The line output (LO)
>> is linked to HPHL so the hardware has some limitations regarding concurrent
>> playback via HPH and LO for instance.
>> 
>> Not all functionality is implemented and there are some number of
>> placeholders. Mostly output functionality like analog and digital mics is
>> missing at this point and implemented in a possible minimal way.
>> The codec driver also uses WCD MBCH framework. The MBHC functionality is
>> also implemented in a minimalistic way to enable IRQs.
>
> Lets try to fill these gaps in next version, I have tested Headset,
> Lineout  and MIC on my side with additional changes to this.

Yep.


>> Despite all limitations in its current state the line out and HPH playback
>> work.
>
> Thanks Alexey for all the work porting the driver from downstream audio
> kernel.
>
> my comments below.

It is a bit difficult to follow comments so if I miss smth please let me know.

>> Cc: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>> ---
>>  sound/soc/codecs/Kconfig      |   19 +
>>  sound/soc/codecs/Makefile     |    8 +
>>  sound/soc/codecs/pm4125-sdw.c |  485 +++++++++++
>>  sound/soc/codecs/pm4125.c     | 1848 +++++++++++++++++++++++++++++++++++++++++
>>  sound/soc/codecs/pm4125.h     |  375 +++++++++
>>  5 files changed, 2735 insertions(+)
>> 
>> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
>> index 6d7e4725d89cd33647770e4f2c4e81445b8335ce..69b08021d165f83f4a7ca18e476cfc7e2473f490 100644
>> --- a/sound/soc/codecs/Kconfig
>> +++ b/sound/soc/codecs/Kconfig
>> @@ -297,6 +297,7 @@ config SND_SOC_ALL_CODECS
>>  	imply SND_SOC_WCD937X_SDW
>>  	imply SND_SOC_WCD938X_SDW
>>  	imply SND_SOC_WCD939X_SDW
>> +	imply SND_SOC_PM4125_SDW
>>  	imply SND_SOC_LPASS_MACRO_COMMON
>>  	imply SND_SOC_LPASS_RX_MACRO
>>  	imply SND_SOC_LPASS_TX_MACRO
>> @@ -2316,6 +2317,24 @@ config SND_SOC_WCD939X_SDW
>>  	  The WCD9390/9395 is a audio codec IC Integrated in
>>  	  Qualcomm SoCs like SM8650.
>>  
>> +config SND_SOC_PM4125
>> +	depends on SND_SOC_PM4125_SDW
>> +	tristate
>> +	depends on SOUNDWIRE || !SOUNDWIRE
>> +	select SND_SOC_WCD_CLASSH
>> +
>> +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_WL1273
>>  	tristate
>>  
>> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
>> index a68c3d192a1b6ccec513c6bc447c29be532ea70c..e993cc9803c4da60daf230a8181673b45c06aa5b 100644
>> --- a/sound/soc/codecs/Makefile
>> +++ b/sound/soc/codecs/Makefile
>> @@ -348,6 +348,8 @@ snd-soc-wcd938x-y := wcd938x.o
>>  snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
>>  snd-soc-wcd939x-y := wcd939x.o
>>  snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
>> +snd-soc-pm4125-y := pm4125.o
>> +snd-soc-pm4125-sdw-y := pm4125-sdw.o
>>  snd-soc-wl1273-y := wl1273.o
>>  snd-soc-wm-adsp-y := wm_adsp.o
>>  snd-soc-wm0010-y := wm0010.o
>> @@ -779,6 +781,12 @@ ifdef CONFIG_SND_SOC_WCD939X_SDW
>>  # 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_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_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
>> diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..d09c4f5fb3b7b7918a1a0c25750046b212f1063f
>> --- /dev/null
>> +++ b/sound/soc/codecs/pm4125-sdw.c
>> @@ -0,0 +1,485 @@
>> +// 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)),
>> +	WCD_SDW_CH(PM4125_COMP_L, PM4125_COMP_PORT, BIT(0)),
>> +	WCD_SDW_CH(PM4125_COMP_R, PM4125_COMP_PORT, BIT(1)),
>> +};
>> +
>> +static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
>> +	WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_PORT, BIT(0)),
>> +	WCD_SDW_CH(PM4125_DMIC0, PM4125_DMIC_0_3_MBHC_PORT, BIT(0)),
>> +	WCD_SDW_CH(PM4125_DMIC1, PM4125_DMIC_0_3_MBHC_PORT, BIT(1)),
>> +	WCD_SDW_CH(PM4125_DMIC2, PM4125_DMIC_0_3_MBHC_PORT, BIT(2)),
> these does not look correct. there are 2 ADCs .

Ok, this needs to be fixed.


[...]


>> +static bool pm4125_rdwr_register(struct device *dev, unsigned int reg)
>> +{
>> +	if (reg > PM4125_ANA_BASE_ADDR &&
>> +	    reg < PM4125_ANALOG_REGISTERS_MAX_SIZE)
>> +		return pm4125_reg_access_analog[PM4125_REG(reg)] & WR_REG;
> Lets not bring in some additional layer here from downstream, pl use the
> registers directly here as we do for other codecs.

Ok, let's fix this.


[...]

>> +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 start 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);
> we do have 100 chars per line, do not split the lines unless required.

Heh, I see, thanks. Checkpatch seems to check for 100 chars now.


[..]

>> diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..58435b9c75865b06b344c6d79800aa9b4bac3abd
>> --- /dev/null
>> +++ b/sound/soc/codecs/pm4125.c
>> @@ -0,0 +1,1848 @@
>> +// 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_params.h>
>> +#include <sound/pcm.h>
>> +#include <sound/soc-dapm.h>
>> +#include <sound/soc.h>
>> +#include <sound/tlv.h>
>> +
>> +#include "wcd-mbhc-v2.h"
>> +#include "pm4125.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,
>> +};
>> +
>> +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 regulator_bulk_data supplies[PM4125_MAX_BULK_SUPPLY];
>> +	struct regulator *buck_supply;
>
> unused.

Needs to be removed, yep.

>> +	struct snd_soc_jack *jack;
>> +	unsigned long status_mask;
>> +	s32 micb_ref[PM4125_MAX_MICBIAS];
>> +	s32 pullup_ref[PM4125_MAX_MICBIAS];
>> +	u32 hph_mode;
>> +	int ear_rx_path;
>> +	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 rx_clk_cnt;
>> +	atomic_t ana_clk_count;
> unused

Ok.


[..]

>> +static int pm4125_handle_post_irq(void *data)
>> +{
>> +	struct pm4125_priv *pm4125;
>> +
>> +	if (data)
>> +		pm4125 = (struct pm4125_priv *)data;
>> +	else
>> +		return IRQ_HANDLED;
> This will result in interrupt storm, as you are not clearning the source.
>
>> +
>> +	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;
>> +}

Do you mean that it should be:

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;
}  

I need to fix irq_drv_data = NULL in pm4125_regmap_irq_chip then.
IIRC it is always NULL.
The same behaviour as in wcd937x.c

>> +static const u32 pm4125_config_regs[] = {
>> +	PM4125_DIG_SWR_INTR_LEVEL_0,
>> +};
>> +
>> +static const 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,
>> +	.irq_drv_data = NULL,
>> +};
>> +
>> +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, 0x03, 0x00);
> pl use defines instead of using magic values.
> applies to most part of the driver.
> also pl take a look at
> snd_soc_component_read/write_field() functions which are handy


Ok, this will be some rework in order to do this.


>> +	/* Enable surge protection */
>> +	regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, 0xC0, 0xC0);
>> +
>> +	/* Disable mic bias pull down */
>> +	regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01, 0x00);
>> +}
>> +
>> +static int pm4125_global_mbias_disable(struct snd_soc_component *component)
>> +{
>> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
>> +				      0x10, 0x00);
>> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
>> +				      0x20, 0x00);
>> +	return 0;
>> +}
>> +
>> +static int pm4125_global_mbias_enable(struct snd_soc_component *component)
>> +{
>> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
>> +				      0x20, 0x20);
>> +	snd_soc_component_update_bits(component, PM4125_ANA_MBIAS_EN,
>> +				      0x10, 0x10);
>> +	usleep_range(1000, 1100);
>> +	return 0;
>> +}
>> +
>> +static int pm4125_rx_clk_enable(struct snd_soc_component *component)
>
> looks like the rxclk can be converted into a proper dapm supply widget.

I'll check that. Thank you.


[..]

>> +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:
>> +		pm4125_rx_clk_enable(component);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_ANA_HPHPA_CNP_CTL_1,
>> +					      0x02, 0x02);
>> +
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_SWR_HPHPA_HD2,
>> +					      0x38, 0x38);
>> +
>> +		set_bit(HPH_COMP_DELAY, &pm4125->status_mask);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMU:
>> +
> drop space.

Ok.

>> +		if (pm4125->comp1_enable) {
>> +			snd_soc_component_update_bits(component,
>> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
>> +						0x02, 0x02);
>> +
>> +			if (pm4125->comp2_enable)
>> +				snd_soc_component_update_bits(component,
>> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
>> +						0x01, 0x01);
>> +			/*
>> +			 * 5ms sleep is required after COMP is enabled as per
>> +			 * HW requirement
>> +			 */
>> +			if (test_bit(HPH_COMP_DELAY, &pm4125->status_mask)) {
>> +				usleep_range(5000, 5100);
>> +				clear_bit(HPH_COMP_DELAY, &pm4125->status_mask);
>> +			}
>> +		} else {
>> +			snd_soc_component_update_bits(component,
>> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
>> +						0x02, 0x00);
>> +		}
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_RX0_CTL,
>> +					      0x80, 0x00);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
>> +					      0x04, 0x04);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
>> +					      0x01, 0x01);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMD:
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_RX_CLK_CTL,
>> +					      0x01, 0x00);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
>> +					      0x04, 0x00);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_RX0_CTL,
>> +					      0x80, 0x80);
>> +		if (pm4125->comp1_enable)
>> +			snd_soc_component_update_bits(component,
>> +						PM4125_DIG_SWR_CDC_COMP_CTL_0,
>> +						0x02, 0x00);
>> +		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);
> this can fit in 100 chars.

Ok.

[...]

>> +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);
>> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
>> +
>> +	switch (event) {
>> +	case SND_SOC_DAPM_PRE_PMU:
>> +		usleep_range(200, 210);
>> +		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
> Am really not following the logic here, we set the bit here and check
> the bit in POST_PMU and then we set BIT in PRE_PMD and clear it int
> POST_PMD.
>
> So this bit will be set in all the cases.
>
> why do we even need this bit to be set, can we not use the dealy by default?

I guess it makes a lot of sense to remove it.


>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
>> +					      0x03, 0x03);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMU:
>> +		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
>> +			usleep_range(5000, 5100);
>> +			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
>> +		}
>> +
>> +		enable_irq(pm4125->hphl_pdm_wd_int);
>> +		break;
>> +	case SND_SOC_DAPM_PRE_PMD:
>> +		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
>> +		set_bit(HPH_PA_DELAY, &pm4125->status_mask);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMD:
>> +		if (test_bit(HPH_PA_DELAY, &pm4125->status_mask)) {
>> +			usleep_range(5000, 5100);
>> +			clear_bit(HPH_PA_DELAY, &pm4125->status_mask);
>> +		}
>> +
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
>> +					      0x03, 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);
>> +	struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
>> +
>> +	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,
>> +					      0x40, 0x40);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_PDM_WD_CTL0,
>> +					      0x03, 0x03);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMU:
>> +		usleep_range(5000, 5010);
>> +
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_ANA_COMBOPA_CTL_4,
>> +					      0x0F, 0x04);
>> +		enable_irq(pm4125->hphl_pdm_wd_int);
>> +		break;
>> +	case SND_SOC_DAPM_PRE_PMD:
>> +		disable_irq_nosync(pm4125->hphl_pdm_wd_int);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMD:
>> +		usleep_range(2000, 2010);
>> +
> Empty lining is really not consistent across the code, either use it
> consistently or not use it at all.

Ok.


[...]

>> +static int pm4125_enable_rx1(struct snd_soc_dapm_widget *w,
>> +			     struct snd_kcontrol *kcontrol, int event)
>> +{
>> +	struct snd_soc_component *component =
>> +					snd_soc_dapm_to_component(w->dapm);
>> +
>> +	if (event == SND_SOC_DAPM_POST_PMD)
>> +		pm4125_rx_clk_disable(component);
>
> if we convert the rx clk into dapm, then things will be inplace
> automatically and you do not do this kinda handling.

Thanks. I need to check this.

>> +
>> +	return 0;
>> +}
>> +
>> +static int pm4125_enable_rx2(struct snd_soc_dapm_widget *w,
>> +			     struct snd_kcontrol *kcontrol, int event)
>> +{
>> +	struct snd_soc_component *component =
>> +					snd_soc_dapm_to_component(w->dapm);
>> +
>> +	if (event == SND_SOC_DAPM_POST_PMD)
>> +		pm4125_rx_clk_disable(component);
>> +
>> +	return 0;
>> +}
>
> This function is duplicate of pm4125_enable_rx1()...

Heh. Ok.

>> +static int pm4125_get_micb_vout_ctl_val(u32 micb_mv)
>> +{
>> +	if (micb_mv < 1600 || micb_mv > 2850) {
>> +		pr_err("Unsupported micbias voltage (%u mV)\n", 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)
>> +{
> Lets add the code needed for ADC in next spin.

Yes.


>> +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;
>> +
>> +	switch (w->shift) {
>> +	case 0:
>> +	case 1:
> Can we use proper names here, instead of 1 and 0, can we use
> PM4125_DMIC0, PM4125_DMIC1 ?


Okay.


>> +		dmic_clk_reg = PM4125_DIG_SWR_CDC_DMIC1_CTL;
> Also why can not we pass this as reg to the widget? then you can avoid
> all this switch caseing.


No idea. Let me check if I'll be able to fix that.


>> +		break;
>> +	default:
>> +		dev_err(component->dev, "Invalid DMIC selection\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (event) {
>> +	case SND_SOC_DAPM_PRE_PMU:
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_AMIC_CTL,
>> +					      0x02, 0x00);
>> +		snd_soc_component_update_bits(component,
>> +					      dmic_clk_reg,
>> +					      0x08, 0x08);
>> +		break;
>> +	case SND_SOC_DAPM_POST_PMD:
>> +		snd_soc_component_update_bits(component,
>> +					      dmic_clk_reg,
>> +					      0x08, 0x00);
>> +		snd_soc_component_update_bits(component,
>> +					      PM4125_DIG_SWR_CDC_AMIC_CTL,
>> +					      0x02, 0x02);
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int pm4125_micbias_control(struct snd_soc_component *component,
>> +				  int micb_num, int req, bool is_dapm)
>> +{
> Lets implement this in v2.


Yes, agree.


>> +static int __pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w,
>> +					 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_ENABLE, true);
> MICBIAS 3 needs internal LDO pull up, as we can not connect it to
> vairable voltage.


Are we missing some code here? Maybe let's see if we can add it then?


>> +		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_DISABLE, true);
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int pm4125_codec_enable_micbias(struct snd_soc_dapm_widget *w,
>> +				       struct snd_kcontrol *kcontrol, int event)
>> +{
>> +	return __pm4125_codec_enable_micbias(w, event);
> Why do we need this boiler plate, same comment applies to other parts of
> the code too.

I tried to make a minimal working version (that doesn't horribly fail) that
we're going to update with more patches during next submission.


[..]

>> +static const struct snd_kcontrol_new hph_type_detect_controls[] = {
>> +	SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, NULL, NULL),
>> +};
>> +
>> +static const struct snd_kcontrol_new impedance_detect_controls[] = {
>> +	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, NULL, NULL),
>> +	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, NULL, NULL),
>
> What are these supposed to read?


Erm, nothing at this point? It is a stub for future changes.


>> +static void pm4125_mbhc_mbhc_bias_control(struct snd_soc_component *component,
>> +					  bool enable)
>> +{
>> +	snd_soc_component_update_bits(component, PM4125_ANA_MBHC_ELECT, 0x01,
>> +				      enable ? 0x01 : 0x00);
>> +}
>> +
>> +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,
>> +					0xfc, vth << 2);
>> +	}
>> +}
>> +
>> +static const struct wcd_mbhc_cb mbhc_cb = {
>> +	.clk_setup = NULL,
>> +	.mbhc_bias = pm4125_mbhc_mbhc_bias_control,
>> +	.set_btn_thr = pm4125_mbhc_program_btn_thr,
>> +	.micbias_enable_status = NULL,
>> +	.hph_pull_up_control_v2 = NULL,
>> +	.mbhc_micbias_control = NULL,
>> +	.mbhc_micb_ramp_control = NULL,
>> +	.mbhc_micb_ctrl_thr_mic = NULL,
>> +	.compute_impedance = NULL,
>> +	.mbhc_gnd_det_ctrl = NULL,
>> +	.hph_pull_down_ctrl = NULL,
>> +	.mbhc_moisture_config = NULL,
>> +	.mbhc_get_moisture_status = NULL,
>> +	.mbhc_moisture_polling_ctrl = NULL,
>> +	.mbhc_moisture_detect_en = NULL,
> Either we add mbhc or not, having these dummy functions is not really
> helping.


Okay, then I can remove it then.


[...]


>> +static bool pm4125_swap_gnd_mic(struct snd_soc_component *component)
>> +{
>> +	return true;
>
> why true all the time? Isn't this suppose to control a mux or a gpio?


I can see if I can remove it or let me know if you have code that implements
this. It was supposed to be a stub.


[...]

>> diff --git a/sound/soc/codecs/pm4125.h b/sound/soc/codecs/pm4125.h
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..2c5e8218202d92a0adc493413368991a406471b0
>> --- /dev/null
>> +++ b/sound/soc/codecs/pm4125.h
>> @@ -0,0 +1,375 @@
>> +/* 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_REG(reg)	((reg > PM4125_DIG_BASE_ADDR) ? \
>> +			 (reg - PM4125_DIG_BASE_ADDR) : \
>> +			 (reg - PM4125_ANA_BASE_ADDR))
>> +
>> +enum {
>> +	REG_NO_ACCESS,
>> +	RD_REG,
>> +	WR_REG,
>> +	RD_WR_REG
>> +};
>> +
> Both PM4125_REG(), and RD/WR_REG does not make sense, please use the
> registers directly in the regmap config callbacks.


Yes, okay.


> ...
>> +#define PM4125_MAX_MICBIAS			3
>> +#define PM4125_MAX_BULK_SUPPLY			4
>> +#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_PORT = 1,
>> +	PM4125_DMIC_0_3_MBHC_PORT,
>> +	PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_0_3_MBHC_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
>> +int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
> Should this be static inline, if not you will endup with multiple
> defination of the function.
> same for other stubs too.

Thanks, I'll check that.


>> +		    struct snd_pcm_substream *substream,
>> +		    struct snd_soc_dai *dai)
>> +{
>> +	return -EOPNOTSUPP;
>> +}
>> +
>> +int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
>> +			      struct snd_soc_dai *dai,
>> +			      void *stream, int direction)
>> +{
>> +	return -EOPNOTSUPP;
>> +}
>> +
>> +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,
>> +	PM4125_ADC3,
>> +	PM4125_DMIC0,
>> +	PM4125_DMIC1,
>> +	PM4125_MBHC,
>> +	PM4125_DMIC2,
>> +	PM4125_DMIC3,
>> +	PM4125_DMIC4,
>> +	PM4125_DMIC5,
>> +	PM4125_DMIC6,
> do we really have so many channels on TX, AFAIU, its only 2 channels and
> two lanes.
>> +};
>> +
>> +enum pm4125_rx_sdw_channels {
>> +	PM4125_HPH_L,
>> +	PM4125_HPH_R,
>> +	PM4125_CLSH,
>> +	PM4125_COMP_L,
>> +	PM4125_COMP_R,
>> +	PM4125_LO,
>> +	PM4125_DSD_R,
>> +	PM4125_DSD_L,
> Do we have so many channes? AFAIU, this is only 4 channels and 1 lane.

I thought it is 2 or 4 channels and 2 lanes for rx.
But for tx it is 2 channels and 1 lane.

Anyways, this needs to be fixed.

Thank you for your comments.

Best regards,
Alexey

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec
  2025-07-01 23:30         ` Alexey Klimov
@ 2025-07-02  6:22           ` Krzysztof Kozlowski
  0 siblings, 0 replies; 25+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-02  6:22 UTC (permalink / raw)
  To: Alexey Klimov
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On 02/07/2025 01:30, Alexey Klimov wrote:
>>>>> +  It has RX and TX Soundwire slave devices. This bindings is for the
>>>>> +  slave devices.
>>>>
>>>> Last sentence is redundant and makes no sense. Codec has only slave
>>>> devices, so how this can be anything else than for slave devices?
>>>
>>> This came from other similar files that describe bindings for child codec nodes
>>> of soundwire nodes. For example from qcom,wcd937x-sdw.yaml.
>>> Should this be rephrased to "This bindings is for the soundwire slave devices." ?
>>
>> You just pasted the same, so I don't get how you want to rephrase into
>> the same sentence.
> 
> Not really.
> Original sentence: "This bindings is for the slave devices."
> Sentence from my email: "This bindings is for the soundwire slave devices."
> 
> The difference is 1 word.
> If it doesn't work, then maybe any suggestions?
> 
> Maybe "This bindings is for audio codec node that must be a child node of the
> associated soundwire master node."?
No, drop, it's not the pattern in the bindings. We don't explain that
I2C device should be in I2C bus, because that's obvious. Saying this is
a Soundwire device should be enough.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-07-02  1:43     ` Alexey Klimov
@ 2025-07-03 12:50       ` Srinivas Kandagatla
  0 siblings, 0 replies; 25+ messages in thread
From: Srinivas Kandagatla @ 2025-07-03 12:50 UTC (permalink / raw)
  To: Alexey Klimov, Srinivas Kandagatla, Liam Girdwood, Mark Brown,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Stephen Boyd
  Cc: Lee Jones, Jaroslav Kysela, Takashi Iwai, linux-arm-msm,
	linux-sound, devicetree, linux-kernel, Dmitry Baryshkov

On 7/2/25 2:43 AM, Alexey Klimov wrote:
>>> +static int pm4125_handle_post_irq(void *data)
>>> +{
>>> +	struct pm4125_priv *pm4125;
>>> +
>>> +	if (data)
>>> +		pm4125 = (struct pm4125_priv *)data;
>>> +	else
>>> +		return IRQ_HANDLED;
>> This will result in interrupt storm, as you are not clearning the source.
>>
>>> +
>>> +	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;
>>> +}
> Do you mean that it should be:
> 
> 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;
> }  

yes. We can not return IRQ_HANDLED without clearing the pending
interrupt source.

--srini
> 
> I need to fix irq_drv_data = NULL in pm4125_regmap_irq_chip then.
> IIRC it is always NULL.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-07-01 21:04       ` Mark Brown
@ 2025-07-10 14:45         ` Alexey Klimov
  0 siblings, 0 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-07-10 14:45 UTC (permalink / raw)
  To: Mark Brown
  Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Tue Jul 1, 2025 at 10:04 PM BST, Mark Brown wrote:
> On Tue, Jul 01, 2025 at 08:35:42PM +0100, Alexey Klimov wrote:
>> On Thu Jun 26, 2025 at 12:56 PM BST, Mark Brown wrote:
>> > On Thu, Jun 26, 2025 at 12:50:31AM +0100, Alexey Klimov wrote:
>
>> >> +static int pm4125_micbias_control(struct snd_soc_component *component,
>> >> +				  int micb_num, int req, bool is_dapm)
>> >> +{
>> >> +	return 0;
>> >> +}
>
>> > Why have this empty function which is only called from within the
>> > driver?  At best it's making the callers look like they do something.
>
>> I tried to make a minimal working version that we're going to
>> update with more patches during next submission.
>
> Add the callers when you need them, right now this is just noise.
> Nobody can tell if the callers make sense since the function does
> nothing.

Ok, I cleaned it for the next version. Thanks.

>> >> +#if defined(CONFIG_OF)
>> >> +static const struct of_device_id pm4125_of_match[] = {
>> >> +	{ .compatible = "qcom,pm4125-codec" },
>> >> +	{ }
>> >> +};
>> >> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
>> >> +#endif
>
>> > Why does this compatible exist?  If the driver is instantiated from a
>> > as a Linux software contruct it shouldn't appear in the DT.
>
>> Could you please elaborate a bit more? Should it be instantiated
>> as an MFD device or platform device?
>
> Yes, if it's the child of a MFD then it shouldn't need to be described
> separately in the DT.

Currently, it is going to be described as child/slave device:

spmi_bus {
	pmic@0 {
		pmic4125_codec: codec {
			...
		}
and will go probably in pm4125.dtsi which lists all child nodes with
compatibles. Not sure if it is because each PMIC is customazable or because
of better maintainability.
Also, might need specific description of regulators which may vary from
board to board. Not sure how is that supposed to be done without device
tree description at this point.

Thanks,
Alexey

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver
  2025-06-26  6:19   ` Krzysztof Kozlowski
@ 2025-07-10 15:37     ` Alexey Klimov
  0 siblings, 0 replies; 25+ messages in thread
From: Alexey Klimov @ 2025-07-10 15:37 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Stephen Boyd, Lee Jones,
	Jaroslav Kysela, Takashi Iwai, linux-arm-msm, linux-sound,
	devicetree, linux-kernel, Dmitry Baryshkov, Srinivas Kandagatla

On Thu Jun 26, 2025 at 7:19 AM BST, Krzysztof Kozlowski wrote:
> On Thu, Jun 26, 2025 at 12:50:31AM +0100, Alexey Klimov wrote:
>> +
>> +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) {
>> +		dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n");
>> +		return -ENODEV;
>> +	}
>> +	of_node_get(pm4125->rxnode);
>
> Where  do you clean this up?

Please don't tell me that this is a bug that being copied from driver
to driver.

I changed it to such flow for the next version since it seems that reference
should be decremented after of_parse_phandle() returns with it incremented:

rxnode = of_parse_phandle();
if (!rxnode)
	return dev_err_probe(...);

component_match_add_release(..., rxnode);
of_node_put(rxnode);


>> +	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) {
>> +		dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n");
>> +			return -ENODEV;
>
> Messed indent. This should be anyway just one line as always - return
> dev_err_probe.

I changed it for the next version as you suggested. Thanks.

>> +	}
>> +	of_node_get(pm4125->txnode);
>
> And this?
>
>> +	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);
>> +
>> +	cfg = &pm4125->mbhc_cfg;
>> +	cfg->swap_gnd_mic = pm4125_swap_gnd_mic;
>> +
>> +	pm4125->supplies[0].supply = "vdd-io";
>> +	pm4125->supplies[1].supply = "vdd-cp";
>> +	pm4125->supplies[2].supply = "vdd-mic-bias";
>> +	pm4125->supplies[3].supply = "vdd-pa-vpos";
>> +
>> +	ret = devm_regulator_bulk_get(dev, PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "Failed to get supplies\n");
>> +
>> +	ret = regulator_bulk_enable(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
>> +	if (ret) {
>> +		regulator_bulk_free(PM4125_MAX_BULK_SUPPLY, pm4125->supplies);
>
> Double free.

Thanks.

>> +		return dev_err_probe(dev, ret, "Failed to enable supplies\n");
>> +	}
>> +
>> +	pm4125_dt_parse_micbias_info(dev, pm4125);
>> +
>> +	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;

[..]

>> +#if defined(CONFIG_OF)
>> +static const struct of_device_id pm4125_of_match[] = {
>> +	{ .compatible = "qcom,pm4125-codec" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, pm4125_of_match);
>> +#endif
>> +
>> +static struct platform_driver pm4125_codec_driver = {
>> +	.probe = pm4125_probe,
>> +	.remove = pm4125_remove,
>> +	.driver = {
>> +		.name = "pm4125_codec",
>> +		.of_match_table = of_match_ptr(pm4125_of_match),
>
> Drop of_match_ptr and #if. We just removed it (or trying to )
> everywhere, so why re-introducing it...

Will remove it. Thanks.

>> +		.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 0000000000000000000000000000000000000000..2c5e8218202d92a0adc493413368991a406471b0
>> --- /dev/null
>> +++ b/sound/soc/codecs/pm4125.h

[...]

>> +const u8 pm4125_reg_access_analog[
>
> No, you cannot have data defined in the header. This is neither style of
> C, nor Linux kernel, nor makes any sense. What if this will be included
> by some other unit? This is some terrible downstream style.
>
> Heh... you actually do include it twice, so you would see all the
> duplicated data for no reason at all.

I pulled in the change that fixes this for the next version.
Thank you.

Best regards,
Alexey


^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2025-07-10 15:37 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-25 23:50 [PATCH 0/3] Add PM4125 audio codec driver Alexey Klimov
2025-06-25 23:50 ` [PATCH 1/3] dt-bindings: sound: add bindings for pm4125 audio codec Alexey Klimov
2025-06-26  1:30   ` Rob Herring (Arm)
2025-06-26 11:08     ` Alexey Klimov
2025-06-26  6:13   ` Krzysztof Kozlowski
2025-06-28 16:41     ` Alexey Klimov
2025-06-29 14:59       ` Dmitry Baryshkov
2025-06-30  8:21       ` Krzysztof Kozlowski
2025-07-01 23:30         ` Alexey Klimov
2025-07-02  6:22           ` Krzysztof Kozlowski
2025-06-25 23:50 ` [PATCH 2/3] dt-bindings: mfd: qcom,spmi-pmic: add " Alexey Klimov
2025-06-26  8:48   ` Krzysztof Kozlowski
2025-06-28 16:42     ` Alexey Klimov
2025-06-29 15:00       ` Dmitry Baryshkov
2025-06-30  8:18       ` Krzysztof Kozlowski
2025-06-25 23:50 ` [PATCH 3/3] ASoC: codecs: add new pm4125 audio codec driver Alexey Klimov
2025-06-26  6:19   ` Krzysztof Kozlowski
2025-07-10 15:37     ` Alexey Klimov
2025-06-26 11:56   ` Mark Brown
2025-07-01 19:35     ` Alexey Klimov
2025-07-01 21:04       ` Mark Brown
2025-07-10 14:45         ` Alexey Klimov
2025-06-28 20:24   ` Srinivas Kandagatla
2025-07-02  1:43     ` Alexey Klimov
2025-07-03 12:50       ` Srinivas Kandagatla

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).