Linux Sound subsystem development
 help / color / mirror / Atom feed
* [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware
@ 2026-11-13 18:38 Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers Neil Armstrong
                   ` (22 more replies)
  0 siblings, 23 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

This patchset adds the necessary plumbing to drive the SM8650 audio
interfaces directly from Linux. This is required for boards like the
Valve Deckard that boot Linux at EL2 without the ADSP audio firmware,
meaning the audio interfaces must be driven directly by the application
processor through lpass-cpu.

Without the ADSP firmware, the LPASS clocks (MI2S bit clocks,
codec macro MCLKs, LPM memory clocks) and memory zones must be
controlled by Linux.

To support this, the series introduces the following changes:
- Bindings: Adds dt-bindings for the SM8650 LPASS core & audio clock
  controllers, LPASS CPU, WSA RX/TX channel ids, and Senary/Septenary MI2S ids.
- Clocks: Adds drivers for the SM8650 LPASS HM, core, audio, and
  AON clock controllers.
- ASoC LPASS core: Prepares the lpass and lpass-cdc-dma drivers to
  handle multiple LPAIFs for I2S ports, allocates and maps LPM buffer
  memory for DAIs, and adds support for the optional WSA interface
  and Senary/Septenary MI2S ports.
- ASoC SM8650: Introduces the SM8650 lpass-cpu variant and allows the
  sc8280xp machine driver to be used with lpass-cpu to keep sound card
  bindings working.
- Device Tree: Refactors the SM8650 dts to move EL1-specific Gunyah nodes
  to a dedicated sm8650-el1.dtsi. Introduces sm8650-lpass-cpu.dtsi for
  boards booting at EL2, and wires up the speakers (Senary MI2S) and
  DMICs (VA macro) on the sm8650-valve-deckard board.
- Defconfig: Enables the SM8650 LPASS CPU driver as a module in arm64 defconfig

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
Neil Armstrong (23):
      dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers
      clk: qcom: add SM8650 LPASS core & audio clock controllers
      dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used
      ASoC: dt-bindings: qcom,lpass: add Senary & Septenary MI2S ids
      ASoC: dt-bindings: qcom,lpass: add WSA RX and TX channels ids
      ASoC: dt-bindings: qcom,lpass-*-macro: document less clock when ADSP is not used
      ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS
      ASoC: qcom: lpass-cpu: get the optional codec_mem3 clock
      ASoC: qcom: lpass: add support for optional WSA interface
      ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA
      ASoC: qcom: lpass: prepare to handle multiple lpaifs for i2s
      ASoC: qcom: lpass: add support for MI2S_SENARY & MI2S_SEPTENARY
      ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs
      ASoC: qcom: lpass: support LPM memory type for I2S LPAIFs
      ASoC: qcom: lpass: allocate LPM buffer memory to DAIs
      ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory
      ASoC: qcom: add SM8650 LPASS CPU driver
      ASoC: qcom: swd: allow LPASS_CDC_DMA_WSA_RX0 as soundwire dai
      ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu
      arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi
      arm64: dts: qcom: add SM8650 LPASS CPU dtsi
      arm64: dts: qcom: sm8650-valve-deckard: enable audio
      arm64: defconfig: enable the SM8650 LPASS CPU driver

 .../bindings/clock/qcom,sm8650-lpasscorecc.yaml    | 179 +++++
 .../pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml     |  20 +-
 .../devicetree/bindings/sound/qcom,lpass-cpu.yaml  |  74 +-
 .../bindings/sound/qcom,lpass-rx-macro.yaml        |  17 +-
 .../bindings/sound/qcom,lpass-tx-macro.yaml        |  20 +-
 .../bindings/sound/qcom,lpass-va-macro.yaml        |  13 +-
 .../bindings/sound/qcom,lpass-wsa-macro.yaml       |  21 +-
 .../boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts      |   1 +
 arch/arm64/boot/dts/qcom/sm8650-el1.dtsi           | 775 ++++++++++++++++++++
 arch/arm64/boot/dts/qcom/sm8650-hdk.dts            |   1 +
 arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi     | 222 ++++++
 arch/arm64/boot/dts/qcom/sm8650-mtp.dts            |   1 +
 arch/arm64/boot/dts/qcom/sm8650-qrd.dts            |   1 +
 arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts  |  85 ++-
 arch/arm64/boot/dts/qcom/sm8650.dtsi               | 799 ++-------------------
 arch/arm64/configs/defconfig                       |   1 +
 drivers/clk/qcom/Kconfig                           |  12 +
 drivers/clk/qcom/Makefile                          |   1 +
 drivers/clk/qcom/lpassaudiocc-sm8650.c             | 777 ++++++++++++++++++++
 drivers/clk/qcom/lpasscorecc-sm8650.c              | 509 +++++++++++++
 .../dt-bindings/clock/qcom,lpassaudiocc-sm8650.h   |  47 ++
 .../dt-bindings/clock/qcom,lpasscorecc-sm8650.h    |  30 +
 include/dt-bindings/sound/qcom,lpass.h             |  24 +
 sound/soc/qcom/Kconfig                             |  12 +
 sound/soc/qcom/Makefile                            |   2 +
 sound/soc/qcom/lpass-apq8016.c                     |   9 +-
 sound/soc/qcom/lpass-cdc-dma.c                     |  47 +-
 sound/soc/qcom/lpass-cpu.c                         | 344 ++++++---
 sound/soc/qcom/lpass-lpaif-reg.h                   |  51 +-
 sound/soc/qcom/lpass-platform.c                    | 482 +++++++++++--
 sound/soc/qcom/lpass-sc7180.c                      |  10 +-
 sound/soc/qcom/lpass-sc7280.c                      |  10 +-
 sound/soc/qcom/lpass-sm8650.c                      | 651 +++++++++++++++++
 sound/soc/qcom/lpass.h                             | 198 ++++-
 sound/soc/qcom/sc8280xp.c                          |  16 +-
 sound/soc/qcom/sdw.c                               |   1 +
 36 files changed, 4471 insertions(+), 992 deletions(-)
---
base-commit: ddfe51b0a0f5dc9e415cc2c8ad8db17532dd426b
change-id: 20261113-topic-sm8650-el2-c6caf4237ca7

Best regards,
--  
Neil Armstrong <neil.armstrong@linaro.org>


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

* [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:57   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 02/23] clk: " Neil Armstrong
                   ` (21 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

The SM8650 LPASS HM, core, audio and AON clock controllers need to be
described to drive audio from Linux without the ADSP firmware.

Document these clock controllers and add their clock & power domain ids.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 .../bindings/clock/qcom,sm8650-lpasscorecc.yaml    | 179 +++++++++++++++++++++
 .../dt-bindings/clock/qcom,lpassaudiocc-sm8650.h   |  47 ++++++
 .../dt-bindings/clock/qcom,lpasscorecc-sm8650.h    |  30 ++++
 3 files changed, 256 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8650-lpasscorecc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8650-lpasscorecc.yaml
new file mode 100644
index 000000000000..9c5207ee02f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,sm8650-lpasscorecc.yaml
@@ -0,0 +1,179 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/qcom,sm8650-lpasscorecc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm LPASS Core & Audio Clock Controllers on SM8650
+
+maintainers:
+  - Neil Armstrong <neil.armstrong@linaro.org>
+
+description: |
+  Qualcomm LPASS HM, core, audio and AON clock controllers provide the clocks
+  and power domains of the Low Power Audio SubSystem (LPASS) on SM8650, used
+  when LPASS is driven by Linux instead of the ADSP firmware.
+
+  See also:
+    include/dt-bindings/clock/qcom,lpasscorecc-sm8650.h
+    include/dt-bindings/clock/qcom,lpassaudiocc-sm8650.h
+
+properties:
+  compatible:
+    enum:
+      - qcom,sm8650-lpassaoncc
+      - qcom,sm8650-lpassaudiocc
+      - qcom,sm8650-lpasscorecc
+      - qcom,sm8650-lpasshm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+
+  power-domains:
+    maxItems: 1
+
+  required-opps:
+    maxItems: 1
+
+  '#clock-cells':
+    const: 1
+
+  '#power-domain-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sm8650-lpasshm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Board XO source
+        clock-names:
+          items:
+            - const: bi_tcxo
+      required:
+        - power-domains
+        - '#power-domain-cells'
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sm8650-lpasscorecc
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Board XO source
+        clock-names:
+          items:
+            - const: bi_tcxo
+        required-opps: false
+      required:
+        - power-domains
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sm8650-lpassaoncc
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Board XO source
+            - description: LPASS_CORE_CC_CORE_CLK
+        clock-names:
+          items:
+            - const: bi_tcxo
+            - const: iface
+        power-domains: false
+        required-opps: false
+      required:
+        - '#power-domain-cells'
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sm8650-lpassaudiocc
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Board XO source
+            - description: LPASS_AON_CC_MAIN_RCG_CLK_SRC
+        clock-names:
+          items:
+            - const: bi_tcxo
+            - const: iface
+        required-opps: false
+      required:
+        - power-domains
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,lpassaudiocc-sm8650.h>
+    #include <dt-bindings/clock/qcom,lpasscorecc-sm8650.h>
+    #include <dt-bindings/clock/qcom,sm8650-gcc.h>
+    #include <dt-bindings/power/qcom,rpmhpd.h>
+
+    clock-controller@6bc0000 {
+        compatible = "qcom,sm8650-lpassaudiocc";
+        reg = <0x06bc0000 0x38000>;
+        clocks = <&bi_tcxo_div2>;
+        clock-names = "bi_tcxo";
+        power-domains = <&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+        #clock-cells = <1>;
+    };
+
+    lpass_aon: clock-controller@6e00000 {
+        compatible = "qcom,sm8650-lpassaoncc";
+        reg = <0x06e00000 0x30000>;
+        clocks = <&bi_tcxo_div2>, <&lpass_core LPASS_CORE_CC_CORE_CLK>;
+        clock-names = "bi_tcxo", "iface";
+        #clock-cells = <1>;
+        #power-domain-cells = <1>;
+    };
+
+    lpass_core: clock-controller@7b00000 {
+        compatible = "qcom,sm8650-lpasscorecc";
+        reg = <0x07b00000 0x58000>;
+        clocks = <&bi_tcxo_div2>;
+        clock-names = "bi_tcxo";
+        power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>;
+        #clock-cells = <1>;
+    };
+
+    lpass_hm: clock-controller@7e00000 {
+        compatible = "qcom,sm8650-lpasshm";
+        reg = <0x07e00000 0xa000>;
+        clocks = <&bi_tcxo_div2>, <&gcc GCC_LPASS_CFG_NOC_SWAY_CLK>;
+        clock-names = "bi_tcxo", "iface";
+        power-domains = <&rpmhpd RPMHPD_LCX>;
+        required-opps = <&rpmhpd_opp_nom>;
+        #clock-cells = <1>;
+        #power-domain-cells = <1>;
+    };
+...
diff --git a/include/dt-bindings/clock/qcom,lpassaudiocc-sm8650.h b/include/dt-bindings/clock/qcom,lpassaudiocc-sm8650.h
new file mode 100644
index 000000000000..c10e2a2639f1
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,lpassaudiocc-sm8650.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DT_BINDINGS_CLK_QCOM_LPASS_AUDIO_CC_SM8650_H
+#define _DT_BINDINGS_CLK_QCOM_LPASS_AUDIO_CC_SM8650_H
+
+/* LPASS_AUDIO_CC clocks */
+#define LPASS_AUDIO_CC_CODEC_MEM_CLK			1
+#define LPASS_AUDIO_CC_CODEC_MEM0_CLK			2
+#define LPASS_AUDIO_CC_CODEC_MEM1_CLK			3
+#define LPASS_AUDIO_CC_CODEC_MEM2_CLK			4
+#define LPASS_AUDIO_CC_CODEC_MEM3_CLK			5
+#define LPASS_AUDIO_CC_RX_MCLK_CLK			6
+#define LPASS_AUDIO_CC_RX_MCLK_CLK_SRC			7
+#define LPASS_AUDIO_CC_TX_MCLK_RX_CLK			8
+#define LPASS_AUDIO_CC_WSA_MCLK_CLK_SRC			9
+#define LPASS_AUDIO_CC_WSA_MCLK_CLK			10
+#define LPASS_AUDIO_CC_WSA2_MCLK_CLK			11
+#define LPASS_AUDIO_CC_TX_MCLK_WSA_CLK			12
+#define LPASS_AUDIO_CC_TX_MCLK_WSA2_CLK			13
+#define LPASS_AUDIO_CC_CDIV_RX_MCLK_DIV_CLK_SRC		16
+#define LPASS_AUDIO_CC_CDIV_WSA_MCLK_DIV_CLK_SRC	17
+#define LPASS_AUDIO_CC_EXT_IF1_CLK_SRC			18
+#define LPASS_AUDIO_CC_EXT_IF1_IBIT_CLK			19
+#define LPASS_AUDIO_CC_EXT_IF2_CLK_SRC			20
+#define LPASS_AUDIO_CC_EXT_IF2_IBIT_CLK			21
+#define LPASS_AUDIO_CC_EXT_IF4_CLK_SRC			22
+#define LPASS_AUDIO_CC_EXT_IF4_IBIT_CLK			23
+#define LPASS_AUDIO_CC_EXT_IF5_CLK_SRC			24
+#define LPASS_AUDIO_CC_EXT_IF5_IBIT_CLK			25
+
+/* LPASS_AON_CC clocks */
+#define LPASS_AON_CC_MAIN_RCG_CLK_SRC			1
+#define LPASS_AON_CC_AUDIO_HM_H_CLK			2
+#define LPASS_AON_CC_TX_MCLK_CLK_SRC			3
+#define LPASS_AON_CC_TX_MCLK_CLK			4
+#define LPASS_AON_CC_VA_MEM0_CLK			5
+#define LPASS_AON_CC_CDIV_TX_MCLK_DIV_CLK_SRC		6
+#define LPASS_AON_CC_EXT_IF1_CLK_SRC			7
+#define LPASS_AON_CC_EXT_IF1_IBIT_CLK			8
+
+/* LPASS_AON_CC power domains */
+#define LPASS_AON_CC_LPASS_AUDIO_HM_GDSC		0
+
+#endif
diff --git a/include/dt-bindings/clock/qcom,lpasscorecc-sm8650.h b/include/dt-bindings/clock/qcom,lpasscorecc-sm8650.h
new file mode 100644
index 000000000000..2849729a2e56
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,lpasscorecc-sm8650.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DT_BINDINGS_CLK_QCOM_LPASS_CORE_CC_SM8650_H
+#define _DT_BINDINGS_CLK_QCOM_LPASS_CORE_CC_SM8650_H
+
+/* LPASS_CORE_CC clocks */
+#define LPASS_CORE_CC_DIG_PLL                           0
+#define LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC      1
+#define LPASS_CORE_CC_DIG_PLL_OUT_ODD                   2
+#define LPASS_CORE_CC_CORE_CLK                          3
+#define LPASS_CORE_CC_CORE_CLK_SRC                      4
+#define LPASS_CORE_CC_EXT_IF0_CLK_SRC                   5
+#define LPASS_CORE_CC_EXT_IF0_IBIT_CLK                  6
+#define LPASS_CORE_CC_EXT_IF1_CLK_SRC                   7
+#define LPASS_CORE_CC_EXT_IF1_IBIT_CLK                  8
+#define LPASS_CORE_CC_EXT_IF2_CLK_SRC                   9
+#define LPASS_CORE_CC_EXT_IF2_IBIT_CLK                  10
+#define LPASS_CORE_CC_LPM_CORE_CLK                      11
+#define LPASS_CORE_CC_LPM_MEM0_CORE_CLK                 12
+#define LPASS_CORE_CC_SYSNOC_MPORT_CORE_CLK             13
+#define LPASS_CORE_CC_EXT_MCLK0_CLK                     14
+#define LPASS_CORE_CC_EXT_MCLK0_CLK_SRC                 15
+
+/* LPASS Core power domains */
+#define LPASS_CORE_HM_GDSC				0
+
+#endif

-- 
2.34.1


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

* [PATCH RFC 02/23] clk: qcom: add SM8650 LPASS core & audio clock controllers
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:14   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 03/23] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used Neil Armstrong
                   ` (20 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Without the ADSP firmware, the LPASS clocks (MI2S bit clocks, codec
macro MCLKs, LPM memory clocks...) must be controlled by Linux.

Add drivers for the SM8650 LPASS HM, core, audio and AON clock
controllers, the HM, audio and AON controllers keeping their iface clock
enabled while in use, LPASS HM's being the GCC LPASS config NoC clock.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/clk/qcom/Kconfig               |  12 +
 drivers/clk/qcom/Makefile              |   1 +
 drivers/clk/qcom/lpassaudiocc-sm8650.c | 777 +++++++++++++++++++++++++++++++++
 drivers/clk/qcom/lpasscorecc-sm8650.c  | 509 +++++++++++++++++++++
 4 files changed, 1299 insertions(+)

diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 27d0ab24d50a..a693948322f8 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -1959,6 +1959,18 @@ config SM_GPUCC_8750
 	  Say Y if you want to support graphics controller devices and
 	  functionality such as 3D graphics.
 
+config SM_LPASS_CORECC_8650
+	tristate "SM8650 LPASS Core & Audio Clock Controller"
+	depends on ARM64 || COMPILE_TEST
+	select SM_GCC_8650
+	select QCOM_GDSC
+	default m if ARCH_QCOM
+	help
+	  Support for the LPASS(Low Power Audio Subsystem) core and audio clock
+	  controller on SM8650 devices.
+	  Say Y if you want to use LPASS clocks and power domains of the LPASS
+	  core clock controller.
+
 config SM_LPASSCC_6115
 	tristate "SM6115 Low Power Audio Subsystem (LPASS) Clock Controller"
 	depends on ARM64 || COMPILE_TEST
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 6affb5073f26..b6862dc0437a 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -213,6 +213,7 @@ obj-$(CONFIG_SM_GPUCC_8550) += gpucc-sm8550.o
 obj-$(CONFIG_SM_GPUCC_8650) += gpucc-sm8650.o
 obj-$(CONFIG_SM_GPUCC_8750) += gpucc-sm8750.o gxclkctl-kaanapali.o
 obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o gxclkctl-kaanapali.o
+obj-$(CONFIG_SM_LPASS_CORECC_8650) += lpasscorecc-sm8650.o lpassaudiocc-sm8650.o
 obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o
 obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o
 obj-$(CONFIG_SM_TCSRCC_8650) += tcsrcc-sm8650.o
diff --git a/drivers/clk/qcom/lpassaudiocc-sm8650.c b/drivers/clk/qcom/lpassaudiocc-sm8650.c
new file mode 100644
index 000000000000..636ce9a1b345
--- /dev/null
+++ b/drivers/clk/qcom/lpassaudiocc-sm8650.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,lpassaudiocc-sm8650.h>
+
+#include "clk-branch.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+#include "clk-regmap-mux.h"
+#include "common.h"
+#include "gdsc.h"
+#include "reset.h"
+
+enum {
+	P_BI_TCXO,
+};
+
+static const struct parent_map lpass_audio_cc_parent_map_0[] = {
+	{ P_BI_TCXO, 0 },
+};
+
+static const struct clk_parent_data lpass_audio_cc_parent_data_0[] = {
+	{ .index = 0 },
+};
+
+static const struct parent_map lpass_aon_cc_parent_map_0[] = {
+	{ P_BI_TCXO, 0 },
+};
+
+static const struct clk_parent_data lpass_aon_cc_parent_data_0[] = {
+	{ .index = 0 },
+};
+
+/* XO only for now: the ADSP plan also uses the audio CC PLLs for MCLKs */
+static const struct freq_tbl ftbl_lpass_mclk_clk_src[] = {
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	{ }
+};
+
+/* XO only for now: the ADSP plan feeds these from the audio CC DIG PLL */
+static const struct freq_tbl ftbl_lpass_cc_ext_clk_src[] = {
+	F(1536000, P_BI_TCXO, 1, 2, 25),
+	F(2304000, P_BI_TCXO, 1, 3, 25),
+	F(3072000, P_BI_TCXO, 1, 4, 25),
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 lpass_aon_cc_ext_if1_clk_src = {
+	.cmd_rcgr = 0xb004,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_cc_ext_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_aon_cc_ext_if1_clk_src",
+		.parent_data = lpass_aon_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_aon_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_aon_cc_main_rcg_clk_src = {
+	.cmd_rcgr = 0x19000,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = lpass_aon_cc_parent_map_0,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_aon_cc_main_rcg_clk_src",
+		.parent_data = lpass_aon_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_aon_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_aon_cc_tx_mclk_clk_src = {
+	.cmd_rcgr = 0x1b004,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = lpass_aon_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_mclk_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_aon_cc_tx_mclk_clk_src",
+		.parent_data = lpass_aon_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_aon_cc_parent_data_0),
+		//.flags = CLK_OPS_PARENT_ENABLE,
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_audio_cc_ext_if1_clk_src = {
+	.cmd_rcgr = 0x18004,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_cc_ext_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_audio_cc_ext_if1_clk_src",
+		.parent_data = lpass_audio_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_audio_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_audio_cc_ext_if2_clk_src = {
+	.cmd_rcgr = 0x19004,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_cc_ext_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_audio_cc_ext_if2_clk_src",
+		.parent_data = lpass_audio_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_audio_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_audio_cc_ext_if4_clk_src = {
+	.cmd_rcgr = 0x1c004,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_cc_ext_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_audio_cc_ext_if4_clk_src",
+		.parent_data = lpass_audio_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_audio_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_audio_cc_ext_if5_clk_src = {
+	.cmd_rcgr = 0x1d004,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_cc_ext_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_audio_cc_ext_if5_clk_src",
+		.parent_data = lpass_audio_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_audio_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_audio_cc_wsa_mclk_clk_src = {
+	.cmd_rcgr = 0x2a004,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_mclk_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_audio_cc_wsa_mclk_clk_src",
+		.parent_data = lpass_audio_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_audio_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_audio_cc_rx_mclk_clk_src = {
+	.cmd_rcgr = 0x2c004,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = lpass_audio_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_mclk_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_audio_cc_rx_mclk_clk_src",
+		.parent_data = lpass_audio_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_audio_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+/* MCLK = RCG / CDIV (reset value: divide by 2), the 2X clocks are the RCG */
+static struct clk_regmap_div lpass_audio_cc_cdiv_rx_mclk_div_clk_src = {
+	.reg = 0x2c0d0,
+	.shift = 0,
+	.width = 4,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "lpass_audio_cc_cdiv_rx_mclk_div_clk_src",
+		.parent_hws = (const struct clk_hw*[]){
+			&lpass_audio_cc_rx_mclk_clk_src.clkr.hw,
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_regmap_div_ro_ops,
+	},
+};
+
+static struct clk_regmap_div lpass_audio_cc_cdiv_wsa_mclk_div_clk_src = {
+	.reg = 0x2a0d0,
+	.shift = 0,
+	.width = 4,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "lpass_audio_cc_cdiv_wsa_mclk_div_clk_src",
+		.parent_hws = (const struct clk_hw*[]){
+			&lpass_audio_cc_wsa_mclk_clk_src.clkr.hw,
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_regmap_div_ro_ops,
+	},
+};
+
+static struct clk_regmap_div lpass_aon_cc_cdiv_tx_mclk_div_clk_src = {
+	.reg = 0x1b010,
+	.shift = 0,
+	.width = 4,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "lpass_aon_cc_cdiv_tx_mclk_div_clk_src",
+		.parent_hws = (const struct clk_hw*[]){
+			&lpass_aon_cc_tx_mclk_clk_src.clkr.hw,
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_regmap_div_ro_ops,
+	},
+};
+
+static struct clk_branch lpass_aon_cc_audio_hm_h_clk;
+
+static struct clk_branch lpass_audio_cc_codec_mem_clk = {
+	.halt_reg = 0x2e000,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2e000,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_codec_mem_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_audio_hm_h_clk.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_codec_mem0_clk = {
+	.halt_reg = 0x2e004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2e004,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_codec_mem0_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_audio_hm_h_clk.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_codec_mem1_clk = {
+	.halt_reg = 0x2e010,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2e010,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_codec_mem1_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_audio_hm_h_clk.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_codec_mem2_clk = {
+	.halt_reg = 0x2e01c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2e01c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_codec_mem2_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_audio_hm_h_clk.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_codec_mem3_clk = {
+	.halt_reg = 0x2e028,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2e028,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_codec_mem3_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_audio_hm_h_clk.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_rx_mclk_clk = {
+	.halt_reg = 0x2c0d4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2c0d4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_rx_mclk_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_cdiv_rx_mclk_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_ext_if1_ibit_clk = {
+	.halt_reg = 0x1801c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1801c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_ext_if1_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_ext_if1_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_ext_if2_ibit_clk = {
+	.halt_reg = 0x1901c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1901c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_ext_if2_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_ext_if2_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_ext_if4_ibit_clk = {
+	.halt_reg = 0x1c01c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1c01c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_ext_if4_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_ext_if4_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_ext_if5_ibit_clk = {
+	.halt_reg = 0x1d01c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1d01c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_ext_if5_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_ext_if5_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_wsa_mclk_clk = {
+	.halt_reg = 0x2a0d4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2a0d4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_wsa_mclk_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_cdiv_wsa_mclk_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_wsa2_mclk_clk = {
+	.halt_reg = 0x2d004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2d004,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_wsa2_mclk_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_audio_cc_wsa_mclk_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_aon_cc_tx_mclk_clk;
+
+static struct clk_branch lpass_audio_cc_tx_mclk_wsa_clk = {
+	.halt_reg = 0x2a0f4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2a0f4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_tx_mclk_wsa_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_tx_mclk_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_tx_mclk_rx_clk = {
+	.halt_reg = 0x2c0e0,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2c0e0,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_tx_mclk_rx_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_tx_mclk_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_audio_cc_tx_mclk_wsa2_clk = {
+	.halt_reg = 0x2d00c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2d00c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_audio_cc_tx_mclk_wsa2_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_tx_mclk_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_aon_cc_ext_if1_ibit_clk = {
+	.halt_reg = 0xb01c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb01c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_aon_cc_ext_if1_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_ext_if1_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_aon_cc_audio_hm_h_clk = {
+	.halt_reg = 0x11014,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x11014,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_aon_cc_audio_hm_h_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_main_rcg_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_aon_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_aon_cc_va_mem0_clk = {
+	.halt_reg = 0x11060,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x11060,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_aon_cc_va_mem0_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_main_rcg_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_aon_cc_tx_mclk_clk = {
+	.halt_reg = 0x1b014,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1b014,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_aon_cc_tx_mclk_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_aon_cc_cdiv_tx_mclk_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct gdsc lpass_aon_cc_lpass_audio_hm_gdsc = {
+	.gdscr = 0x11090,
+	.pd = {
+		.name = "lpass_aon_cc_lpass_audio_hm_gdsc",
+	},
+	.pwrsts = PWRSTS_OFF_ON,
+	.flags = RETAIN_FF_ENABLE,
+};
+
+static struct clk_regmap *lpass_aon_cc_sm8650_clocks[] = {
+	[LPASS_AON_CC_MAIN_RCG_CLK_SRC] = &lpass_aon_cc_main_rcg_clk_src.clkr,
+	[LPASS_AON_CC_AUDIO_HM_H_CLK] = &lpass_aon_cc_audio_hm_h_clk.clkr,
+	[LPASS_AON_CC_VA_MEM0_CLK] = &lpass_aon_cc_va_mem0_clk.clkr,
+	[LPASS_AON_CC_TX_MCLK_CLK_SRC] = &lpass_aon_cc_tx_mclk_clk_src.clkr,
+	[LPASS_AON_CC_TX_MCLK_CLK] = &lpass_aon_cc_tx_mclk_clk.clkr,
+	[LPASS_AON_CC_CDIV_TX_MCLK_DIV_CLK_SRC] = &lpass_aon_cc_cdiv_tx_mclk_div_clk_src.clkr,
+	[LPASS_AON_CC_EXT_IF1_CLK_SRC] = &lpass_aon_cc_ext_if1_clk_src.clkr,
+	[LPASS_AON_CC_EXT_IF1_IBIT_CLK] = &lpass_aon_cc_ext_if1_ibit_clk.clkr,
+};
+
+static struct gdsc *lpass_aon_cc_sm8650_gdscs[] = {
+	[LPASS_AON_CC_LPASS_AUDIO_HM_GDSC] = &lpass_aon_cc_lpass_audio_hm_gdsc,
+};
+
+static struct clk_regmap *lpass_audio_cc_sm8650_clocks[] = {
+	[LPASS_AUDIO_CC_WSA_MCLK_CLK_SRC] = &lpass_audio_cc_wsa_mclk_clk_src.clkr,
+	[LPASS_AUDIO_CC_RX_MCLK_CLK] = &lpass_audio_cc_rx_mclk_clk.clkr,
+	[LPASS_AUDIO_CC_RX_MCLK_CLK_SRC] = &lpass_audio_cc_rx_mclk_clk_src.clkr,
+	[LPASS_AUDIO_CC_WSA_MCLK_CLK] = &lpass_audio_cc_wsa_mclk_clk.clkr,
+	[LPASS_AUDIO_CC_WSA2_MCLK_CLK] = &lpass_audio_cc_wsa2_mclk_clk.clkr,
+	[LPASS_AUDIO_CC_CODEC_MEM_CLK] = &lpass_audio_cc_codec_mem_clk.clkr,
+	[LPASS_AUDIO_CC_CODEC_MEM0_CLK] = &lpass_audio_cc_codec_mem0_clk.clkr,
+	[LPASS_AUDIO_CC_CODEC_MEM1_CLK] = &lpass_audio_cc_codec_mem1_clk.clkr,
+	[LPASS_AUDIO_CC_CODEC_MEM2_CLK] = &lpass_audio_cc_codec_mem2_clk.clkr,
+	[LPASS_AUDIO_CC_CODEC_MEM3_CLK] = &lpass_audio_cc_codec_mem3_clk.clkr,
+	[LPASS_AUDIO_CC_TX_MCLK_WSA_CLK] = &lpass_audio_cc_tx_mclk_wsa_clk.clkr,
+	[LPASS_AUDIO_CC_TX_MCLK_RX_CLK] = &lpass_audio_cc_tx_mclk_rx_clk.clkr,
+	[LPASS_AUDIO_CC_TX_MCLK_WSA2_CLK] = &lpass_audio_cc_tx_mclk_wsa2_clk.clkr,
+	[LPASS_AUDIO_CC_CDIV_RX_MCLK_DIV_CLK_SRC] = &lpass_audio_cc_cdiv_rx_mclk_div_clk_src.clkr,
+	[LPASS_AUDIO_CC_CDIV_WSA_MCLK_DIV_CLK_SRC] = &lpass_audio_cc_cdiv_wsa_mclk_div_clk_src.clkr,
+	[LPASS_AUDIO_CC_EXT_IF1_CLK_SRC] = &lpass_audio_cc_ext_if1_clk_src.clkr,
+	[LPASS_AUDIO_CC_EXT_IF1_IBIT_CLK] = &lpass_audio_cc_ext_if1_ibit_clk.clkr,
+	[LPASS_AUDIO_CC_EXT_IF2_CLK_SRC] = &lpass_audio_cc_ext_if2_clk_src.clkr,
+	[LPASS_AUDIO_CC_EXT_IF2_IBIT_CLK] = &lpass_audio_cc_ext_if2_ibit_clk.clkr,
+	[LPASS_AUDIO_CC_EXT_IF4_CLK_SRC] = &lpass_audio_cc_ext_if4_clk_src.clkr,
+	[LPASS_AUDIO_CC_EXT_IF4_IBIT_CLK] = &lpass_audio_cc_ext_if4_ibit_clk.clkr,
+	[LPASS_AUDIO_CC_EXT_IF5_CLK_SRC] = &lpass_audio_cc_ext_if5_clk_src.clkr,
+	[LPASS_AUDIO_CC_EXT_IF5_IBIT_CLK] = &lpass_audio_cc_ext_if5_ibit_clk.clkr,
+};
+
+static const struct regmap_config lpass_audio_cc_sm8650_regmap_config = {
+	.name = "lpasscc_audio",
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = 0x2f000,
+	.fast_io = true,
+};
+
+static const struct qcom_cc_desc lpass_audio_cc_sm8650_desc = {
+	.config = &lpass_audio_cc_sm8650_regmap_config,
+	.clks = lpass_audio_cc_sm8650_clocks,
+	.num_clks = ARRAY_SIZE(lpass_audio_cc_sm8650_clocks),
+};
+
+static const struct of_device_id lpass_audio_cc_sm8650_match_table[] = {
+	{ .compatible = "qcom,sm8650-lpassaudiocc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lpass_audio_cc_sm8650_match_table);
+
+static int lpass_audio_cc_sm8650_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = devm_pm_runtime_enable(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = devm_pm_clk_create(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = pm_clk_add(&pdev->dev, "iface");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to acquire iface clock\n");
+		return ret;
+	}
+
+	ret = pm_runtime_resume_and_get(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = qcom_cc_probe(pdev, &lpass_audio_cc_sm8650_desc);
+
+	pm_runtime_put_sync(&pdev->dev);
+
+	return ret;
+}
+
+static const struct dev_pm_ops lpass_audio_cc_sm8650_pm_ops = {
+	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
+};
+
+static struct platform_driver lpass_audio_cc_sm8650_driver = {
+	.probe = lpass_audio_cc_sm8650_probe,
+	.driver = {
+		.name = "lpass_audio_cc-sm8650",
+		.of_match_table = lpass_audio_cc_sm8650_match_table,
+		.pm = &lpass_audio_cc_sm8650_pm_ops,
+	},
+};
+
+static const struct regmap_config lpass_aon_cc_sm8650_regmap_config = {
+	.name = "lpasscc_aon",
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = 0x2f000,
+	.fast_io = true,
+};
+
+static const struct qcom_cc_desc lpass_aon_cc_sm8650_desc = {
+	.config = &lpass_aon_cc_sm8650_regmap_config,
+	.clks = lpass_aon_cc_sm8650_clocks,
+	.num_clks = ARRAY_SIZE(lpass_aon_cc_sm8650_clocks),
+	.gdscs = lpass_aon_cc_sm8650_gdscs,
+	.num_gdscs = ARRAY_SIZE(lpass_aon_cc_sm8650_gdscs),
+};
+
+static int lpass_aon_cc_sm8650_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = devm_pm_runtime_enable(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = devm_pm_clk_create(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = pm_clk_add(&pdev->dev, "iface");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to acquire iface clock\n");
+		return ret;
+	}
+
+	ret = pm_runtime_resume_and_get(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = qcom_cc_probe(pdev, &lpass_aon_cc_sm8650_desc);
+
+	pm_runtime_put_sync(&pdev->dev);
+
+	return ret;
+}
+
+static const struct of_device_id lpass_aon_cc_sm8650_match_table[] = {
+	{ .compatible = "qcom,sm8650-lpassaoncc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lpass_aon_cc_sm8650_match_table);
+
+static struct platform_driver lpass_aon_cc_sm8650_driver = {
+	.probe = lpass_aon_cc_sm8650_probe,
+	.driver = {
+		.name = "lpass_aon_cc-sm8650",
+		.of_match_table = lpass_aon_cc_sm8650_match_table,
+		.pm = &lpass_audio_cc_sm8650_pm_ops,
+	},
+};
+
+static int __init lpass_audio_cc_sm8650_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&lpass_aon_cc_sm8650_driver);
+	if (ret)
+		return ret;
+
+	return platform_driver_register(&lpass_audio_cc_sm8650_driver);
+}
+subsys_initcall(lpass_audio_cc_sm8650_init);
+
+static void __exit lpass_audio_cc_sm8650_exit(void)
+{
+	platform_driver_unregister(&lpass_audio_cc_sm8650_driver);
+	platform_driver_unregister(&lpass_aon_cc_sm8650_driver);
+}
+module_exit(lpass_audio_cc_sm8650_exit);
+
+MODULE_DESCRIPTION("LPASS Audio CC SM8650 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/qcom/lpasscorecc-sm8650.c b/drivers/clk/qcom/lpasscorecc-sm8650.c
new file mode 100644
index 000000000000..4f91ab0d772e
--- /dev/null
+++ b/drivers/clk/qcom/lpasscorecc-sm8650.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,lpasscorecc-sm8650.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+#include "common.h"
+#include "gdsc.h"
+
+enum {
+	P_BI_TCXO,
+	P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN,
+	P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC,
+	P_LPASS_CORE_CC_DIG_PLL_OUT_ODD,
+};
+
+static const struct pll_vco lucid_ole_vco[] = {
+	{ 249600000, 2300000000, 0 },
+};
+
+static const struct alpha_pll_config lpass_core_cc_dig_pll_config = {
+	/* 614.4 MHz */
+	.l = 0x20,
+	.alpha = 0x0,
+	.config_ctl_val = 0x20485699,
+	.config_ctl_hi_val = 0x00182261,
+	.config_ctl_hi1_val = 0x82aa299c,
+	.test_ctl_val = 0x00000000,
+	.test_ctl_hi_val = 0x00000003,
+	.test_ctl_hi1_val = 0x00009000,
+	.test_ctl_hi2_val = 0x00000034,
+	.user_ctl_val = 0x00000000,
+	.user_ctl_hi_val = 0x00000005,
+};
+
+static struct clk_alpha_pll lpass_core_cc_dig_pll = {
+	.offset = 0x0,
+	.config = &lpass_core_cc_dig_pll_config,
+	.vco_table = lucid_ole_vco,
+	.num_vco = ARRAY_SIZE(lucid_ole_vco),
+	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+	.clkr = {
+		.hw.init = &(struct clk_init_data){
+			.name = "lpass_core_cc_dig_pll",
+			.parent_data = &(const struct clk_parent_data){
+				.index = 0,
+			},
+			.num_parents = 1,
+			.ops = &clk_alpha_pll_lucid_evo_ops,
+		},
+	},
+};
+
+static const struct clk_div_table post_div_table_lpass_core_cc_dig_pll_out_odd[] = {
+	{ 0x5, 5 },
+	{ }
+};
+
+static struct clk_alpha_pll_postdiv lpass_core_cc_dig_pll_out_odd = {
+	.offset = 0x0,
+	.post_div_shift = 14,
+	.post_div_table = post_div_table_lpass_core_cc_dig_pll_out_odd,
+	.num_post_div = ARRAY_SIZE(post_div_table_lpass_core_cc_dig_pll_out_odd),
+	.width = 4,
+	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "lpass_core_cc_dig_pll_out_odd",
+		.parent_hws = (const struct clk_hw*[]){
+			&lpass_core_cc_dig_pll.clkr.hw,
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_alpha_pll_postdiv_lucid_ops,
+	},
+};
+
+static struct clk_regmap_div lpass_core_cc_dig_pll_out_main_div_clk_src = {
+	.reg = 0x10008,
+	.shift = 0,
+	.width = 4,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "lpass_core_cc_dig_pll_out_main_div_clk_src",
+		.parent_hws = (const struct clk_hw*[]){
+			&lpass_core_cc_dig_pll.clkr.hw,
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_regmap_div_ro_ops,
+	},
+};
+
+static const struct parent_map lpass_core_cc_parent_map_0[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_LPASS_CORE_CC_DIG_PLL_OUT_ODD, 5 },
+};
+
+static const struct clk_parent_data lpass_core_cc_parent_data_0[] = {
+	{ .index = 0 },
+	{ .hw = &lpass_core_cc_dig_pll_out_odd.clkr.hw },
+};
+
+static const struct parent_map lpass_core_cc_parent_map_2[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN, 1 },
+	{ P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC, 2 },
+};
+
+static const struct clk_parent_data lpass_core_cc_parent_data_ao_2[] = {
+	{ .index = 1 },
+	{ .hw = &lpass_core_cc_dig_pll.clkr.hw },
+	{ .hw = &lpass_core_cc_dig_pll_out_main_div_clk_src.clkr.hw },
+};
+
+static const struct freq_tbl ftbl_lpass_core_cc_core_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(51200000, P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC, 6, 0, 0),
+	F(102400000, P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC, 3, 0, 0),
+	F(204800000, P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN, 3, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 lpass_core_cc_core_clk_src = {
+	.cmd_rcgr = 0x25000,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = lpass_core_cc_parent_map_2,
+	.freq_tbl = ftbl_lpass_core_cc_core_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_core_cc_core_clk_src",
+		.parent_data = lpass_core_cc_parent_data_ao_2,
+		.num_parents = ARRAY_SIZE(lpass_core_cc_parent_data_ao_2),
+		.ops = &clk_rcg2_shared_ops,
+	},
+};
+
+static const struct freq_tbl ftbl_lpass_core_cc_ext_if0_clk_src[] = {
+	F(1536000, P_BI_TCXO, 1, 2, 25),
+	F(2304000, P_BI_TCXO, 1, 3, 25),
+	F(3072000, P_BI_TCXO, 1, 4, 25),
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 lpass_core_cc_ext_if0_clk_src = {
+	.cmd_rcgr = 0x18000,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_core_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_core_cc_ext_if0_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_core_cc_ext_if0_clk_src",
+		.parent_data = lpass_core_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_core_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_core_cc_ext_if1_clk_src = {
+	.cmd_rcgr = 0x19000,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_core_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_core_cc_ext_if0_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_core_cc_ext_if1_clk_src",
+		.parent_data = lpass_core_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_core_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_core_cc_ext_if2_clk_src = {
+	.cmd_rcgr = 0x1a000,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = lpass_core_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_core_cc_ext_if0_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_core_cc_ext_if2_clk_src",
+		.parent_data = lpass_core_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_core_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_rcg2 lpass_core_cc_ext_mclk0_clk_src = {
+	.cmd_rcgr = 0x28000,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = lpass_core_cc_parent_map_0,
+	.freq_tbl = ftbl_lpass_core_cc_ext_if0_clk_src,
+	.clkr.hw.init = &(const struct clk_init_data){
+		.name = "lpass_core_cc_ext_mclk0_clk_src",
+		.parent_data = lpass_core_cc_parent_data_0,
+		.num_parents = ARRAY_SIZE(lpass_core_cc_parent_data_0),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_branch lpass_core_cc_core_clk = {
+	.halt_reg = 0x27000,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x27000,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x27000,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_core_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_core_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_aon_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_ext_if0_ibit_clk = {
+	.halt_reg = 0x18018,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x18018,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_ext_if0_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_ext_if0_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_ext_if1_ibit_clk = {
+	.halt_reg = 0x19018,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x19018,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_ext_if1_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_ext_if1_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_ext_if2_ibit_clk = {
+	.halt_reg = 0x1a018,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1a018,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_ext_if2_ibit_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_ext_if2_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_lpm_core_clk = {
+	.halt_reg = 0x26000,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x26000,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_lpm_core_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_core_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_lpm_mem0_core_clk = {
+	.halt_reg = 0x26004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x26004,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_lpm_mem0_core_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_core_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_ext_mclk0_clk = {
+	.halt_reg = 0x28014,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x28014,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_ext_mclk0_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_ext_mclk0_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch lpass_core_cc_sysnoc_mport_core_clk = {
+	.halt_reg = 0x2b000,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x2b000,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x2b000,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data){
+			.name = "lpass_core_cc_sysnoc_mport_core_clk",
+			.parent_hws = (const struct clk_hw*[]){
+				&lpass_core_cc_core_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct gdsc lpass_core_cc_lpass_core_hm_gdsc = {
+	.gdscr = 0x0,
+	.pd = {
+		.name = "lpass_core_cc_lpass_core_hm_gdsc",
+	},
+	.pwrsts = PWRSTS_OFF_ON,
+	.flags = RETAIN_FF_ENABLE,
+};
+
+static struct clk_regmap *lpass_core_cc_sm8650_clocks[] = {
+	[LPASS_CORE_CC_CORE_CLK] = &lpass_core_cc_core_clk.clkr,
+	[LPASS_CORE_CC_CORE_CLK_SRC] = &lpass_core_cc_core_clk_src.clkr,
+	[LPASS_CORE_CC_DIG_PLL] = &lpass_core_cc_dig_pll.clkr,
+	[LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC] =
+		&lpass_core_cc_dig_pll_out_main_div_clk_src.clkr,
+	[LPASS_CORE_CC_DIG_PLL_OUT_ODD] = &lpass_core_cc_dig_pll_out_odd.clkr,
+	[LPASS_CORE_CC_EXT_IF0_CLK_SRC] = &lpass_core_cc_ext_if0_clk_src.clkr,
+	[LPASS_CORE_CC_EXT_IF0_IBIT_CLK] = &lpass_core_cc_ext_if0_ibit_clk.clkr,
+	[LPASS_CORE_CC_EXT_IF1_CLK_SRC] = &lpass_core_cc_ext_if1_clk_src.clkr,
+	[LPASS_CORE_CC_EXT_IF1_IBIT_CLK] = &lpass_core_cc_ext_if1_ibit_clk.clkr,
+	[LPASS_CORE_CC_EXT_IF2_CLK_SRC] = &lpass_core_cc_ext_if2_clk_src.clkr,
+	[LPASS_CORE_CC_EXT_IF2_IBIT_CLK] = &lpass_core_cc_ext_if2_ibit_clk.clkr,
+	[LPASS_CORE_CC_LPM_CORE_CLK] = &lpass_core_cc_lpm_core_clk.clkr,
+	[LPASS_CORE_CC_LPM_MEM0_CORE_CLK] = &lpass_core_cc_lpm_mem0_core_clk.clkr,
+	[LPASS_CORE_CC_SYSNOC_MPORT_CORE_CLK] = &lpass_core_cc_sysnoc_mport_core_clk.clkr,
+	[LPASS_CORE_CC_EXT_MCLK0_CLK] = &lpass_core_cc_ext_mclk0_clk.clkr,
+	[LPASS_CORE_CC_EXT_MCLK0_CLK_SRC] = &lpass_core_cc_ext_mclk0_clk_src.clkr,
+};
+
+static const struct regmap_config lpass_core_cc_sm8650_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = 0x56014,
+	.fast_io = true,
+};
+
+static struct clk_alpha_pll *lpass_core_cc_sm8650_plls[] = {
+	&lpass_core_cc_dig_pll,
+};
+
+static const struct qcom_cc_driver_data lpass_core_cc_sm8650_driver_data = {
+	.alpha_plls = lpass_core_cc_sm8650_plls,
+	.num_alpha_plls = ARRAY_SIZE(lpass_core_cc_sm8650_plls),
+};
+
+static const struct qcom_cc_desc lpass_core_cc_sm8650_desc = {
+	.config = &lpass_core_cc_sm8650_regmap_config,
+	.clks = lpass_core_cc_sm8650_clocks,
+	.num_clks = ARRAY_SIZE(lpass_core_cc_sm8650_clocks),
+	.driver_data = &lpass_core_cc_sm8650_driver_data,
+	.use_rpm = true,
+};
+
+static const struct of_device_id lpass_core_cc_sm8650_match_table[] = {
+	{ .compatible = "qcom,sm8650-lpasscorecc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lpass_core_cc_sm8650_match_table);
+
+static struct gdsc *lpass_core_hm_sm8650_gdscs[] = {
+	[LPASS_CORE_HM_GDSC] = &lpass_core_cc_lpass_core_hm_gdsc,
+};
+
+static const struct regmap_config lpass_core_hm_sm8650_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = 0x9000,
+	.fast_io = true,
+};
+
+static const u32 lpass_core_hm_sm8650_critical_cbcrs[] = {
+	0x9000, /* LPASS_TOP_CC_LPASS_CORE_SWAY_AHB_LS */
+};
+
+static const struct qcom_cc_driver_data lpass_core_hm_sm8650_driver_data = {
+	.clk_cbcrs = lpass_core_hm_sm8650_critical_cbcrs,
+	.num_clk_cbcrs = ARRAY_SIZE(lpass_core_hm_sm8650_critical_cbcrs),
+};
+
+static const struct qcom_cc_desc lpass_core_hm_sm8650_desc = {
+	.config = &lpass_core_hm_sm8650_regmap_config,
+	.gdscs = lpass_core_hm_sm8650_gdscs,
+	.num_gdscs = ARRAY_SIZE(lpass_core_hm_sm8650_gdscs),
+	.driver_data = &lpass_core_hm_sm8650_driver_data,
+	.use_rpm = true,
+};
+
+static int lpass_core_cc_sm8650_probe(struct platform_device *pdev)
+{
+	return qcom_cc_probe(pdev, &lpass_core_cc_sm8650_desc);
+}
+
+static struct platform_driver lpass_core_cc_sm8650_driver = {
+	.probe = lpass_core_cc_sm8650_probe,
+	.driver = {
+		.name = "lpass_core_cc-sm8650",
+		.of_match_table = lpass_core_cc_sm8650_match_table,
+	},
+};
+
+static int lpass_hm_core_probe(struct platform_device *pdev)
+{
+	return qcom_cc_probe(pdev, &lpass_core_hm_sm8650_desc);
+}
+
+static const struct dev_pm_ops lpass_hm_sm8650_pm_ops = {
+	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
+};
+
+static const struct of_device_id lpass_hm_sm8650_match_table[] = {
+	{ .compatible = "qcom,sm8650-lpasshm" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lpass_hm_sm8650_match_table);
+
+static struct platform_driver lpass_hm_sm8650_driver = {
+	.probe = lpass_hm_core_probe,
+	.driver = {
+		.name = "lpass_hm-sm8650",
+		.of_match_table = lpass_hm_sm8650_match_table,
+		.pm = &lpass_hm_sm8650_pm_ops,
+	},
+};
+
+static int __init lpass_core_cc_sm8650_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&lpass_hm_sm8650_driver);
+	if (ret)
+		return ret;
+
+	return platform_driver_register(&lpass_core_cc_sm8650_driver);
+}
+subsys_initcall(lpass_core_cc_sm8650_init);
+
+static void __exit lpass_core_cc_sm8650_exit(void)
+{
+	platform_driver_unregister(&lpass_core_cc_sm8650_driver);
+	platform_driver_unregister(&lpass_hm_sm8650_driver);
+}
+module_exit(lpass_core_cc_sm8650_exit);
+
+MODULE_DESCRIPTION("QTI LPASS_CORE_CC SM8650 Driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


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

* [PATCH RFC 03/23] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 02/23] clk: " Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:02   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 04/23] ASoC: dt-bindings: qcom,lpass: add Senary & Septenary MI2S ids Neil Armstrong
                   ` (19 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

On platforms where the ADSP is not used and the LPASS TLMM
is entirely handled by the OS, the LPASS TLMM block requires
a different set of clocks and power-domains to replace the
clocks exposed by the ADSP firmware interface.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 .../pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml       | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
index 1bf08860a4ba..4c54f873199c 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
@@ -29,11 +29,25 @@ properties:
       - description: LPASS LPI TLMM Control and Status registers
 
   clocks:
-    items:
-      - description: LPASS Core voting clock
-      - description: LPASS Audio voting clock
+    oneOf:
+      - items:   # for ADSP based platforms
+          - description: LPASS Core voting clock
+          - description: LPASS Audio voting clock
+      - items:   # for ADSP bypass based platforms
+          - description: LPASS Core voting clock
 
   clock-names:
+    oneOf:
+      - items:   # for ADSP based platforms
+          - const: core
+          - const: audio
+      - items:   # for ADSP bypass based platforms
+          - const: core
+
+  power-domains:
+    maxItems: 2
+
+  power-domain-names:
     items:
       - const: core
       - const: audio

-- 
2.34.1


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

* [PATCH RFC 04/23] ASoC: dt-bindings: qcom,lpass: add Senary & Septenary MI2S ids
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (2 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 03/23] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 05/23] ASoC: dt-bindings: qcom,lpass: add WSA RX and TX channels ids Neil Armstrong
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Some SoCs like SM8650 have Senary & Septenary MI2S interfaces, which can't
be referenced by the LPASS CPU DAI ids.

Add the MI2S_SENARY & MI2S_SEPTENARY DAI ids.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 include/dt-bindings/sound/qcom,lpass.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/dt-bindings/sound/qcom,lpass.h b/include/dt-bindings/sound/qcom,lpass.h
index a9404c3b8884..22f5e1fcc8a6 100644
--- a/include/dt-bindings/sound/qcom,lpass.h
+++ b/include/dt-bindings/sound/qcom,lpass.h
@@ -41,6 +41,9 @@
 #define LPASS_CDC_DMA_VA_TX7 32
 #define LPASS_CDC_DMA_VA_TX8 33
 
+#define MI2S_SENARY	34
+#define MI2S_SEPTENARY	35
+
 #define LPASS_MCLK0	0
 
 #endif /* __DT_QCOM_LPASS_H */

-- 
2.34.1


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

* [PATCH RFC 05/23] ASoC: dt-bindings: qcom,lpass: add WSA RX and TX channels ids
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (3 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 04/23] ASoC: dt-bindings: qcom,lpass: add Senary & Septenary MI2S ids Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 06/23] ASoC: dt-bindings: qcom,lpass-*-macro: document less clock when ADSP is not used Neil Armstrong
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Add the bindings to connect to the Qualcomm WSA interfaces.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 include/dt-bindings/sound/qcom,lpass.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/dt-bindings/sound/qcom,lpass.h b/include/dt-bindings/sound/qcom,lpass.h
index 22f5e1fcc8a6..5d8db5195dad 100644
--- a/include/dt-bindings/sound/qcom,lpass.h
+++ b/include/dt-bindings/sound/qcom,lpass.h
@@ -44,6 +44,27 @@
 #define MI2S_SENARY	34
 #define MI2S_SEPTENARY	35
 
+#define LPASS_CDC_DMA_WSA_RX0 36
+#define LPASS_CDC_DMA_WSA_RX1 37
+#define LPASS_CDC_DMA_WSA_RX2 38
+#define LPASS_CDC_DMA_WSA_RX3 39
+#define LPASS_CDC_DMA_WSA_RX4 40
+#define LPASS_CDC_DMA_WSA_RX5 41
+#define LPASS_CDC_DMA_WSA_RX6 42
+#define LPASS_CDC_DMA_WSA_RX7 43
+#define LPASS_CDC_DMA_WSA_RX8 44
+#define LPASS_CDC_DMA_WSA_RX9 45
+
+#define LPASS_CDC_DMA_WSA_TX0 46
+#define LPASS_CDC_DMA_WSA_TX1 47
+#define LPASS_CDC_DMA_WSA_TX2 48
+#define LPASS_CDC_DMA_WSA_TX3 49
+#define LPASS_CDC_DMA_WSA_TX4 50
+#define LPASS_CDC_DMA_WSA_TX5 51
+#define LPASS_CDC_DMA_WSA_TX6 52
+#define LPASS_CDC_DMA_WSA_TX7 53
+#define LPASS_CDC_DMA_WSA_TX8 54
+
 #define LPASS_MCLK0	0
 
 #endif /* __DT_QCOM_LPASS_H */

-- 
2.34.1


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

* [PATCH RFC 06/23] ASoC: dt-bindings: qcom,lpass-*-macro: document less clock when ADSP is not used
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (4 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 05/23] ASoC: dt-bindings: qcom,lpass: add WSA RX and TX channels ids Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 07/23] ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS Neil Armstrong
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

On platforms where the ADSP is not used and the RX/TX/VA/WSA macros
are entirely handled by the OS, the macros blocks requires a different
set of clocks and power-domains to replace the clocks exposed by the
ADSP firmware interface.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 .../bindings/sound/qcom,lpass-rx-macro.yaml         | 17 +++++++++++------
 .../bindings/sound/qcom,lpass-tx-macro.yaml         | 20 ++++++++++++--------
 .../bindings/sound/qcom,lpass-va-macro.yaml         | 13 ++++++++-----
 .../bindings/sound/qcom,lpass-wsa-macro.yaml        | 21 +++++++++++++--------
 4 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
index 4988e7ed6e34..b76c5dfc0462 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
@@ -129,14 +129,19 @@ allOf:
     then:
       properties:
         clocks:
-          minItems: 4
+          minItems: 3
           maxItems: 4
         clock-names:
-          items:
-            - const: mclk
-            - const: macro
-            - const: dcodec
-            - const: fsgen
+          oneOf:
+            - items:   # for ADSP based platforms
+                - const: mclk
+                - const: macro
+                - const: dcodec
+                - const: fsgen
+            - items:   # for ADSP bypass based platforms
+                - const: mclk
+                - const: macro
+                - const: fsgen
 
 unevaluatedProperties: false
 
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
index d8682ff2e82c..bb150baf3ec9 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
@@ -38,11 +38,11 @@ properties:
     const: 0
 
   clocks:
-    minItems: 3
+    minItems: 2
     maxItems: 5
 
   clock-names:
-    minItems: 3
+    minItems: 2
     maxItems: 5
 
   clock-output-names:
@@ -133,14 +133,18 @@ allOf:
     then:
       properties:
         clocks:
-          minItems: 4
+          minItems: 2
           maxItems: 4
         clock-names:
-          items:
-            - const: mclk
-            - const: macro
-            - const: dcodec
-            - const: fsgen
+          oneOf:
+            - items:   # for ADSP based platforms
+                - const: mclk
+                - const: macro
+                - const: dcodec
+                - const: fsgen
+            - items:   # for ADSP bypass based platforms
+                - const: mclk
+                - const: fsgen
 
 unevaluatedProperties: false
 
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
index aea31fbdad37..bae257447e25 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -160,13 +160,16 @@ allOf:
     then:
       properties:
         clocks:
-          minItems: 3
+          minItems: 1
           maxItems: 3
         clock-names:
-          items:
-            - const: mclk
-            - const: macro
-            - const: dcodec
+          oneOf:
+            - items:   # for ADSP based platforms
+                - const: mclk
+                - const: macro
+                - const: dcodec
+            - items:   # for ADSP bypass based platforms
+                - const: mclk
 
 unevaluatedProperties: false
 
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
index 9fedd80532e2..2ed35d812790 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
@@ -38,11 +38,11 @@ properties:
     const: 0
 
   clocks:
-    minItems: 4
+    minItems: 3
     maxItems: 6
 
   clock-names:
-    minItems: 4
+    minItems: 3
     maxItems: 6
 
   clock-output-names:
@@ -93,14 +93,19 @@ allOf:
     then:
       properties:
         clocks:
-          minItems: 4
+          minItems: 3
           maxItems: 4
         clock-names:
-          items:
-            - const: mclk
-            - const: macro
-            - const: dcodec
-            - const: fsgen
+          oneOf:
+            - items:   # for ADSP based platforms
+                - const: mclk
+                - const: macro
+                - const: dcodec
+                - const: fsgen
+            - items:   # for ADSP bypass based platforms
+                - const: mclk
+                - const: macro
+                - const: fsgen
 
 unevaluatedProperties: false
 

-- 
2.34.1


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

* [PATCH RFC 07/23] ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (5 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 06/23] ASoC: dt-bindings: qcom,lpass-*-macro: document less clock when ADSP is not used Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:57   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 08/23] ASoC: qcom: lpass-cpu: get the optional codec_mem3 clock Neil Armstrong
                   ` (15 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

The SM8650 LPASS can be driven by Linux through lpass-cpu, with extra LPAIF
instances, LPMs and clocks compared to the other SoCs.

Document the SM8650 LPASS CPU registers, clocks and interrupts.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 .../devicetree/bindings/sound/qcom,lpass-cpu.yaml  | 74 ++++++++++++++++++++--
 1 file changed, 68 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
index 3a559bd07a79..f431173a0e88 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
@@ -23,32 +23,33 @@ properties:
       - qcom,apq8016-lpass-cpu
       - qcom,sc7180-lpass-cpu
       - qcom,sc7280-lpass-cpu
+      - qcom,sm8650-lpass-cpu
 
   reg:
     minItems: 1
-    maxItems: 6
+    maxItems: 8
     description: LPAIF core registers
 
   reg-names:
     minItems: 1
-    maxItems: 6
+    maxItems: 8
 
   clocks:
     minItems: 3
-    maxItems: 10
+    maxItems: 16
 
   clock-names:
     minItems: 1
-    maxItems: 10
+    maxItems: 16
 
   interrupts:
     minItems: 1
-    maxItems: 4
+    maxItems: 6
     description: LPAIF DMA buffer interrupt
 
   interrupt-names:
     minItems: 1
-    maxItems: 4
+    maxItems: 6
 
   qcom,adsp:
     $ref: /schemas/types.yaml#/definitions/phandle
@@ -59,6 +60,8 @@ properties:
     maxItems: 3
     description: Phandle to apps_smmu node with sid mask
 
+  dma-coherent: true
+
   power-domains:
     maxItems: 1
 
@@ -241,6 +244,65 @@ allOf:
         - iommus
         - power-domains
 
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: qcom,sm8650-lpass-cpu
+
+    then:
+      properties:
+        clocks:
+          minItems: 16
+          maxItems: 16
+        clock-names:
+          items:
+            - const: aon_cc_audio_hm_h
+            - const: core_cc_sysnoc_mport_core
+            - const: core_cc_ext_mclk0
+            - const: core_cc_ext_if0_ibit
+            - const: core_cc_ext_if1_ibit
+            - const: aon_cc_ext_if1_ibit
+            - const: audio_cc_ext_if1_ibit
+            - const: audio_cc_ext_if2_ibit
+            - const: audio_cc_ext_if4_ibit
+            - const: audio_cc_ext_if5_ibit
+            - const: audio_cc_codec_mem
+            - const: audio_cc_codec_mem0
+            - const: audio_cc_codec_mem1
+            - const: audio_cc_codec_mem2
+            - const: audio_cc_codec_mem3
+            - const: aon_cc_va_mem0
+        reg:
+          minItems: 8
+          maxItems: 8
+        reg-names:
+          items:
+            - const: lpass-hdmiif
+            - const: lpass-lpaif
+            - const: lpass-rxtx-cdc-dma-lpm
+            - const: lpass-rxtx-lpaif
+            - const: lpass-va-lpaif
+            - const: lpass-va-cdc-dma-lpm
+            - const: lpass-wsa-lpaif
+            - const: lpass-aud-lpaif
+        interrupts:
+          minItems: 6
+          maxItems: 6
+        interrupt-names:
+          items:
+            - const: lpass-irq-lpaif
+            - const: lpass-irq-hdmi
+            - const: lpass-irq-vaif
+            - const: lpass-irq-rxtxif
+            - const: lpass-irq-wsaif
+            - const: lpass-irq-audif
+        power-domain-names: false
+
+      required:
+        - iommus
+        - power-domains
+
 examples:
   - |
     #include <dt-bindings/sound/sc7180-lpass.h>

-- 
2.34.1


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

* [PATCH RFC 08/23] ASoC: qcom: lpass-cpu: get the optional codec_mem3 clock
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (6 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 07/23] ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 09/23] ASoC: qcom: lpass: add support for optional WSA interface Neil Armstrong
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Some SoCs like SM8650 have a 4th codec LPM block, gated by its own
codec_mem3 clock, which the driver doesn't get.

Get the optional audio_cc_codec_mem3 clock with the other codec DMA clocks.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cpu.c | 4 ++++
 sound/soc/qcom/lpass.h     | 1 +
 2 files changed, 5 insertions(+)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index ee187eeb7392..ea8b6083529c 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1071,6 +1071,10 @@ static int of_lpass_cdc_dma_clks_parse(struct device *dev,
 	if (IS_ERR(data->codec_mem2))
 		return PTR_ERR(data->codec_mem2);
 
+	data->codec_mem3 = devm_clk_get_optional(dev, "audio_cc_codec_mem3");
+	if (IS_ERR(data->codec_mem3))
+		return PTR_ERR(data->codec_mem3);
+
 	data->va_mem0 = devm_clk_get(dev, "aon_cc_va_mem0");
 	if (IS_ERR(data->va_mem0))
 		return PTR_ERR(data->va_mem0);
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 68b71039b981..4231466928d4 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -107,6 +107,7 @@ struct lpass_data {
 	struct clk *codec_mem0;
 	struct clk *codec_mem1;
 	struct clk *codec_mem2;
+	struct clk *codec_mem3;
 	struct clk *va_mem0;
 
 	/* MI2S SD lines to use for playback/capture */

-- 
2.34.1


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

* [PATCH RFC 09/23] ASoC: qcom: lpass: add support for optional WSA interface
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (7 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 08/23] ASoC: qcom: lpass-cpu: get the optional codec_mem3 clock Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:11   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 10/23] ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA Neil Armstrong
                   ` (13 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Newer SoCs have an additional WSA LPAIF used to driver and
additional soundwire bus dedicated to Speakers.

Add the necessary plumbing which is very similar to the
RXTX support used to driver the WCD soundwire bus.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cpu.c       |  31 +++++++
 sound/soc/qcom/lpass-lpaif-reg.h |  51 +++++++++--
 sound/soc/qcom/lpass-platform.c  | 182 +++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/lpass.h           |  64 ++++++++++++++
 4 files changed, 323 insertions(+), 5 deletions(-)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index ea8b6083529c..d46b72c926b2 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -983,6 +983,16 @@ static struct regmap_config lpass_va_regmap_config = {
 	.cache_type = REGCACHE_FLAT,
 };
 
+static struct regmap_config lpass_wsa_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.writeable_reg = lpass_rxtx_regmap_writeable,
+	.readable_reg = lpass_rxtx_regmap_readable,
+	.volatile_reg = lpass_rxtx_regmap_volatile,
+	.cache_type = REGCACHE_FLAT,
+};
+
 static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
 						struct device_node *node,
 						const char *name)
@@ -1045,6 +1055,8 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
 			data->hdmi_port_enable = 1;
 		} else if (is_cdc_dma_port(id)) {
 			data->codec_dma_enable = 1;
+			if (is_wsa_cdc_dma_port(id))
+				data->wsa_enable = 1;
 		} else {
 			data->mi2s_playback_sd_mode[id] =
 				of_lpass_cpu_parse_sd_lines(dev, node,
@@ -1124,6 +1136,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 		if (IS_ERR(drvdata->va_lpaif))
 			return PTR_ERR(drvdata->va_lpaif);
 
+		if (drvdata->wsa_enable) {
+			drvdata->wsa_lpaif =
+				devm_platform_ioremap_resource_byname(pdev, "lpass-wsa-lpaif");
+			if (IS_ERR(drvdata->wsa_lpaif))
+				return PTR_ERR(drvdata->wsa_lpaif);
+		}
+
 		lpass_rxtx_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant,
 					variant->rxtx_wrdma_channels +
 					variant->rxtx_wrdma_channel_start, LPASS_CDC_DMA_TX3);
@@ -1142,6 +1161,18 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 		if (IS_ERR(drvdata->va_lpaif_map))
 			return PTR_ERR(drvdata->va_lpaif_map);
 
+		if (drvdata->wsa_enable) {
+			lpass_wsa_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant,
+						variant->wsa_wrdma_channels +
+						variant->wsa_wrdma_channel_start,
+						LPASS_CDC_DMA_WSA_TX0);
+
+			drvdata->wsa_lpaif_map = devm_regmap_init_mmio(dev, drvdata->wsa_lpaif,
+								       &lpass_wsa_regmap_config);
+			if (IS_ERR(drvdata->wsa_lpaif_map))
+				return PTR_ERR(drvdata->wsa_lpaif_map);
+		}
+
 		ret = of_lpass_cdc_dma_clks_parse(dev, drvdata);
 		if (ret) {
 			dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
index 6d9d9d1f6a4d..faea5a0ce5ea 100644
--- a/sound/soc/qcom/lpass-lpaif-reg.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -82,6 +82,14 @@
 #define LPAIF_RXTX_IRQSTAT_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x4, port)
 #define LPAIF_RXTX_IRQCLEAR_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0xC, port)
 
+/* LPAIF WSA IRQ */
+#define LPAIF_WSA_IRQ_REG_ADDR(v, addr, port) \
+		(v->wsa_irq_reg_base + (addr) + v->wsa_irq_reg_stride * (port))
+
+#define LPAIF_WSA_IRQEN_REG(v, port) LPAIF_WSA_IRQ_REG_ADDR(v, 0x0, port)
+#define LPAIF_WSA_IRQSTAT_REG(v, port) LPAIF_WSA_IRQ_REG_ADDR(v, 0x4, port)
+#define LPAIF_WSA_IRQCLEAR_REG(v, port) LPAIF_WSA_IRQ_REG_ADDR(v, 0xC, port)
+
 /* LPAIF VA IRQ */
 #define LPAIF_VA_IRQ_REG_ADDR(v, addr, port) \
 		(v->va_irq_reg_base + (addr) + v->va_irq_reg_stride * (port))
@@ -182,7 +190,9 @@
 #define LPAIF_CDC_RDMA_REG_ADDR(v, addr, chan, dai_id) \
 	(is_rxtx_cdc_dma_port(dai_id) ? \
 	(v->rxtx_rdma_reg_base + (addr) + v->rxtx_rdma_reg_stride * (chan)) : \
-	(v->va_rdma_reg_base + (addr) + v->va_rdma_reg_stride * (chan)))
+	(is_wsa_cdc_dma_port(dai_id) ? \
+	 (v->wsa_rdma_reg_base + (addr) + v->wsa_rdma_reg_stride * (chan)) : \
+	 (v->va_rdma_reg_base + (addr) + v->va_rdma_reg_stride * (chan))))
 
 #define LPAIF_CDC_RXTX_RDMACTL_REG(v, chan, dai_id) \
 		LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id)
@@ -205,12 +215,28 @@
 #define LPAIF_CDC_VA_RDMA_INTF_REG(v, chan, dai_id) \
 	LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id)
 
+#define LPAIF_CDC_WSA_RDMACTL_REG(v, chan, dai_id) \
+		LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id)
+#define LPAIF_CDC_WSA_RDMABASE_REG(v, chan, dai_id) \
+		LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id)
+#define LPAIF_CDC_WSA_RDMABUFF_REG(v, chan, dai_id) \
+		LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id)
+#define LPAIF_CDC_WSA_RDMACURR_REG(v, chan, dai_id) \
+		LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
+#define LPAIF_CDC_WSA_RDMAPER_REG(v, chan, dai_id) \
+		LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id)
+#define LPAIF_CDC_WSA_RDMA_INTF_REG(v, chan, dai_id) \
+	LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id)
+
 #define LPAIF_CDC_WRDMA_REG_ADDR(v, addr, chan, dai_id) \
 	(is_rxtx_cdc_dma_port(dai_id) ? \
 	(v->rxtx_wrdma_reg_base + (addr) + \
 		v->rxtx_wrdma_reg_stride * (chan - v->rxtx_wrdma_channel_start)) : \
-	(v->va_wrdma_reg_base + (addr) + \
-		v->va_wrdma_reg_stride * (chan - v->va_wrdma_channel_start)))
+	(is_wsa_cdc_dma_port(dai_id) ? \
+	 (v->wsa_wrdma_reg_base + (addr) + \
+		v->wsa_wrdma_reg_stride * (chan - v->wsa_wrdma_channel_start)) : \
+	 (v->va_wrdma_reg_base + (addr) + \
+		v->va_wrdma_reg_stride * (chan - v->va_wrdma_channel_start))))
 
 #define LPAIF_CDC_RXTX_WRDMACTL_REG(v, chan, dai_id) \
 	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id)
@@ -238,13 +264,28 @@
 #define LPAIF_CDC_VA_WRDMA_INTF_REG(v, chan, dai_id) \
 	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id)
 
+#define LPAIF_CDC_WSA_WRDMACTL_REG(v, chan, dai_id) \
+	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id)
+#define LPAIF_CDC_WSA_WRDMABASE_REG(v, chan, dai_id) \
+	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id)
+#define LPAIF_CDC_WSA_WRDMABUFF_REG(v, chan, dai_id) \
+	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id)
+#define LPAIF_CDC_WSA_WRDMACURR_REG(v, chan, dai_id) \
+	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
+#define LPAIF_CDC_WSA_WRDMAPER_REG(v, chan, dai_id) \
+	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id)
+#define LPAIF_CDC_WSA_WRDMA_INTF_REG(v, chan, dai_id) \
+	LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id)
+
 #define __LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) \
 		(is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_RDMA##reg##_REG(v, chan, dai_id) : \
-			LPAIF_CDC_VA_RDMA##reg##_REG(v, chan, dai_id))
+		 (is_wsa_cdc_dma_port(dai_id) ? LPAIF_CDC_WSA_RDMA##reg##_REG(v, chan, dai_id) : \
+			LPAIF_CDC_VA_RDMA##reg##_REG(v, chan, dai_id)))
 
 #define __LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id) \
 		(is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_WRDMA##reg##_REG(v, chan, dai_id) : \
-			LPAIF_CDC_VA_WRDMA##reg##_REG(v, chan, dai_id))
+		 (is_wsa_cdc_dma_port(dai_id) ? LPAIF_CDC_WSA_WRDMA##reg##_REG(v, chan, dai_id) : \
+			LPAIF_CDC_VA_WRDMA##reg##_REG(v, chan, dai_id)))
 
 #define __LPAIF_CDC_DMA_REG(v, chan, dir, reg, dai_id) \
 		((dir ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index e162627d6f86..9c879a8090bb 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -97,6 +97,30 @@ static const struct snd_pcm_hardware lpass_platform_va_hardware = {
 	.fifo_size		=	0,
 };
 
+static const struct snd_pcm_hardware lpass_platform_wsa_hardware = {
+	.info			=	SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_PAUSE |
+					SNDRV_PCM_INFO_RESUME,
+	.formats		=	SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+	.rates			=	SNDRV_PCM_RATE_8000_192000,
+	.rate_min		=	8000,
+	.rate_max		=	192000,
+	.channels_min		=	1,
+	.channels_max		=	8,
+	.buffer_bytes_max	=	LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE,
+	.period_bytes_max	=	LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
+						LPASS_PLATFORM_PERIODS,
+	.period_bytes_min	=	LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
+						LPASS_PLATFORM_PERIODS,
+	.periods_min		=	LPASS_PLATFORM_PERIODS,
+	.periods_max		=	LPASS_PLATFORM_PERIODS,
+	.fifo_size		=	0,
+};
+
 static int lpass_platform_alloc_rxtx_dmactl_fields(struct device *dev,
 					 struct regmap *map)
 {
@@ -141,6 +165,33 @@ static int lpass_platform_alloc_va_dmactl_fields(struct device *dev,
 					    &v->va_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
 }
 
+static int lpass_platform_alloc_wsa_dmactl_fields(struct device *dev,
+						  struct regmap *map)
+{
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	const struct lpass_variant *v = drvdata->variant;
+	struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
+	int rval;
+
+	rd_dmactl = devm_kzalloc(dev, sizeof(*rd_dmactl), GFP_KERNEL);
+	if (!rd_dmactl)
+		return -ENOMEM;
+
+	wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL);
+	if (!wr_dmactl)
+		return -ENOMEM;
+
+	drvdata->wsa_rd_dmactl = rd_dmactl;
+	drvdata->wsa_wr_dmactl = wr_dmactl;
+
+	rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf,
+					    &v->wsa_rdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
+	if (rval)
+		return rval;
+
+	return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
+					    &v->wsa_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
+}
 
 static int lpass_platform_alloc_dmactl_fields(struct device *dev,
 					 struct regmap *map)
@@ -237,6 +288,11 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 		map = drvdata->va_lpaif_map;
 		drvdata->va_substream[dma_ch] = substream;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		map = drvdata->wsa_lpaif_map;
+		drvdata->wsa_substream[dma_ch] = substream;
+		break;
 	default:
 		break;
 	}
@@ -265,6 +321,12 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 		runtime->dma_bytes = lpass_platform_va_hardware.buffer_bytes_max;
 		snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		snd_soc_set_runtime_hwparams(substream, &lpass_platform_wsa_hardware);
+		runtime->dma_bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
+		snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+		break;
 	default:
 		break;
 	}
@@ -307,6 +369,10 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component,
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
 		drvdata->va_substream[data->dma_ch] = NULL;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		drvdata->wsa_substream[data->dma_ch] = NULL;
+		break;
 	default:
 		break;
 	}
@@ -345,6 +411,12 @@ static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_subst
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
 		dmactl = drvdata->va_wr_dmactl;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		dmactl = drvdata->wsa_rd_dmactl;
+		break;
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		dmactl = drvdata->wsa_wr_dmactl;
+		break;
 	}
 
 	return dmactl;
@@ -380,6 +452,12 @@ static int __lpass_get_id(const struct snd_pcm_substream *substream,
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
 		id = pcm_data->dma_ch - v->va_wrdma_channel_start;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		id = pcm_data->dma_ch;
+		break;
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		id = pcm_data->dma_ch - v->wsa_wrdma_channel_start;
+		break;
 	}
 
 	return id;
@@ -407,6 +485,10 @@ static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream *
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
 		map = drvdata->va_lpaif_map;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		map = drvdata->wsa_lpaif_map;
+		break;
 	}
 
 	return map;
@@ -491,6 +573,8 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
 	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
 		break;
 	default:
 		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai_id);
@@ -756,6 +840,21 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
 			val_mask = LPAIF_IRQ_ALL(ch);
 			val_irqen = LPAIF_IRQ_ALL(ch);
 			break;
+		case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+			ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to rdmactl reg field: %d\n", ret);
+				return ret;
+			}
+			reg_irqclr = LPAIF_WSA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+			val_irqclr = LPAIF_IRQ_ALL(ch);
+
+			reg_irqen = LPAIF_WSA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+			val_mask = LPAIF_IRQ_ALL(ch);
+			val_irqen = LPAIF_IRQ_ALL(ch);
+			break;
 		default:
 			dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
 			return -EINVAL;
@@ -838,6 +937,22 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
 			val_mask = LPAIF_IRQ_ALL(ch);
 			val_irqen = LPAIF_IRQ_ALL(ch);
 			break;
+		case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+			ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to rdmactl reg field: %d\n", ret);
+				return ret;
+			}
+
+			reg_irqclr = LPAIF_WSA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+			val_irqclr = LPAIF_IRQ_ALL(ch);
+
+			reg_irqen = LPAIF_WSA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
+			val_mask = LPAIF_IRQ_ALL(ch);
+			val_irqen = LPAIF_IRQ_ALL(ch);
+			break;
 		default:
 			dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
 			return -EINVAL;
@@ -964,6 +1079,12 @@ static irqreturn_t lpass_dma_interrupt_handler(
 		reg = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 		val = 0;
 	break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		map = drvdata->wsa_lpaif_map;
+		reg = LPAIF_WSA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
+		val = 0;
+	break;
 	default:
 	dev_err(soc_runtime->dev, "%s: invalid  %d interface\n", __func__, dai_id);
 	return -EINVAL;
@@ -1124,6 +1245,31 @@ static irqreturn_t lpass_platform_vaif_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t lpass_platform_wsaif_irq(int irq, void *data)
+{
+	struct lpass_data *drvdata = data;
+	const struct lpass_variant *v = drvdata->variant;
+	unsigned int irqs;
+	irqreturn_t rv;
+	int chan;
+
+	rv = regmap_read(drvdata->wsa_lpaif_map,
+			 LPAIF_WSA_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+
+	/* Handle per channel interrupts */
+	for (chan = 0; chan < LPASS_MAX_CDC_DMA_CHANNELS; chan++) {
+		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->wsa_substream[chan]) {
+			rv = lpass_dma_interrupt_handler(
+						drvdata->wsa_substream[chan],
+						drvdata, chan, irqs);
+			if (rv != IRQ_HANDLED)
+				return rv;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *component,
 						  struct snd_pcm *pcm, int dai_id)
 {
@@ -1156,6 +1302,14 @@ static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *comp
 		buf->bytes = lpass_platform_va_hardware.buffer_bytes_max;
 		buf->addr = drvdata->va_cdc_dma_lpm_buf;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		buf->bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
+		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+		break;
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		buf->bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
+		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 3;
+		break;
 	default:
 		break;
 	}
@@ -1359,6 +1513,34 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 				"error initializing va dmactl fields: %d\n", ret);
 			return ret;
 		}
+
+		if (drvdata->wsa_enable) {
+			ret = regmap_write(drvdata->wsa_lpaif_map,
+				LPAIF_WSA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0x0);
+			if (ret) {
+				dev_err(&pdev->dev, "error writing to wsa irqen reg: %d\n", ret);
+				return ret;
+			}
+			drvdata->wsaif_irq = platform_get_irq_byname(pdev, "lpass-irq-wsaif");
+			if (drvdata->wsaif_irq < 0)
+				return -ENODEV;
+
+			ret = devm_request_irq(&pdev->dev, drvdata->wsaif_irq,
+					       lpass_platform_wsaif_irq, 0,
+					       "lpass-irq-wsaif", drvdata);
+			if (ret) {
+				dev_err(&pdev->dev, "wsa irq request failed: %d\n", ret);
+				return ret;
+			}
+
+			ret = lpass_platform_alloc_wsa_dmactl_fields(&pdev->dev,
+								     drvdata->wsa_lpaif_map);
+			if (ret) {
+				dev_err(&pdev->dev,
+					"error initializing wsa dmactl fields: %d\n", ret);
+				return ret;
+			}
+		}
 	}
 
 	if (drvdata->hdmi_port_enable) {
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 4231466928d4..46ae02e67403 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -46,6 +46,8 @@ static inline bool is_cdc_dma_port(int dai_id)
 	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
 	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
 		return true;
 	}
 	return false;
@@ -61,6 +63,16 @@ static inline bool is_rxtx_cdc_dma_port(int dai_id)
 	return false;
 }
 
+static inline bool is_wsa_cdc_dma_port(int dai_id)
+{
+	switch (dai_id) {
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		return true;
+	}
+	return false;
+}
+
 struct lpaif_i2sctl {
 	struct regmap_field *loopback;
 	struct regmap_field *spken;
@@ -119,12 +131,14 @@ struct lpass_data {
 
 	int hdmi_port_enable;
 	int codec_dma_enable;
+	int wsa_enable;
 
 	/* low-power audio interface (LPAIF) registers */
 	void __iomem *lpaif;
 	void __iomem *hdmiif;
 	void __iomem *rxtx_lpaif;
 	void __iomem *va_lpaif;
+	void __iomem *wsa_lpaif;
 
 	u32 rxtx_cdc_dma_lpm_buf;
 	u32 va_cdc_dma_lpm_buf;
@@ -134,12 +148,14 @@ struct lpass_data {
 	struct regmap *hdmiif_map;
 	struct regmap *rxtx_lpaif_map;
 	struct regmap *va_lpaif_map;
+	struct regmap *wsa_lpaif_map;
 
 	/* interrupts from the low-power audio interface (LPAIF) */
 	int lpaif_irq;
 	int hdmiif_irq;
 	int rxtxif_irq;
 	int vaif_irq;
+	int wsaif_irq;
 
 	/* SOC specific variations in the LPASS IP integration */
 	const struct lpass_variant *variant;
@@ -149,12 +165,14 @@ struct lpass_data {
 	unsigned long hdmi_dma_ch_bit_map;
 	unsigned long rxtx_dma_ch_bit_map;
 	unsigned long va_dma_ch_bit_map;
+	unsigned long wsa_dma_ch_bit_map;
 
 	/* used it for handling interrupt per dma channel */
 	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
 	struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS];
 	struct snd_pcm_substream *rxtx_substream[LPASS_MAX_CDC_DMA_CHANNELS];
 	struct snd_pcm_substream *va_substream[LPASS_MAX_CDC_DMA_CHANNELS];
+	struct snd_pcm_substream *wsa_substream[LPASS_MAX_CDC_DMA_CHANNELS];
 
 	/* SOC specific clock list */
 	struct clk_bulk_data *clks;
@@ -170,6 +188,8 @@ struct lpass_data {
 	struct lpaif_dmactl *rxtx_rd_dmactl;
 	struct lpaif_dmactl *rxtx_wr_dmactl;
 	struct lpaif_dmactl *va_wr_dmactl;
+	struct lpaif_dmactl *wsa_rd_dmactl;
+	struct lpaif_dmactl *wsa_wr_dmactl;
 
 	/* Regmap fields of HDMI_CTRL registers*/
 	struct regmap_field *hdmitx_legacy_en;
@@ -215,6 +235,15 @@ struct lpass_variant {
 	u32	va_wrdma_reg_base;
 	u32	va_wrdma_reg_stride;
 	u32	va_wrdma_channels;
+	u32	wsa_irq_reg_base;
+	u32	wsa_irq_reg_stride;
+	u32	wsa_irq_ports;
+	u32	wsa_rdma_reg_base;
+	u32	wsa_rdma_reg_stride;
+	u32	wsa_rdma_channels;
+	u32	wsa_wrdma_reg_base;
+	u32	wsa_wrdma_reg_stride;
+	u32	wsa_wrdma_channels;
 	u32	i2sctrl_reg_base;
 	u32	i2sctrl_reg_stride;
 	u32	i2s_ports;
@@ -369,6 +398,40 @@ struct lpass_variant {
 	struct reg_field va_wrdma_codec_ch;
 	struct reg_field va_wrdma_codec_fs_delay;
 
+	/* CDC WSA RD_DMA */
+	struct reg_field wsa_rdma_intf;
+	struct reg_field wsa_rdma_bursten;
+	struct reg_field wsa_rdma_wpscnt;
+	struct reg_field wsa_rdma_fifowm;
+	struct reg_field wsa_rdma_enable;
+	struct reg_field wsa_rdma_dyncclk;
+	struct reg_field wsa_rdma_burst8;
+	struct reg_field wsa_rdma_burst16;
+	struct reg_field wsa_rdma_dynburst;
+	struct reg_field wsa_rdma_codec_enable;
+	struct reg_field wsa_rdma_codec_pack;
+	struct reg_field wsa_rdma_codec_intf;
+	struct reg_field wsa_rdma_codec_fs_sel;
+	struct reg_field wsa_rdma_codec_ch;
+	struct reg_field wsa_rdma_codec_fs_delay;
+
+	/* CDC WSA WR_DMA */
+	struct reg_field wsa_wrdma_intf;
+	struct reg_field wsa_wrdma_bursten;
+	struct reg_field wsa_wrdma_wpscnt;
+	struct reg_field wsa_wrdma_fifowm;
+	struct reg_field wsa_wrdma_enable;
+	struct reg_field wsa_wrdma_dyncclk;
+	struct reg_field wsa_wrdma_burst8;
+	struct reg_field wsa_wrdma_burst16;
+	struct reg_field wsa_wrdma_dynburst;
+	struct reg_field wsa_wrdma_codec_enable;
+	struct reg_field wsa_wrdma_codec_pack;
+	struct reg_field wsa_wrdma_codec_intf;
+	struct reg_field wsa_wrdma_codec_fs_sel;
+	struct reg_field wsa_wrdma_codec_ch;
+	struct reg_field wsa_wrdma_codec_fs_delay;
+
 	/**
 	 * on SOCs like APQ8016 the channel control bits start
 	 * at different offset to ipq806x
@@ -377,6 +440,7 @@ struct lpass_variant {
 	u32	wrdma_channel_start;
 	u32	rxtx_wrdma_channel_start;
 	u32	va_wrdma_channel_start;
+	u32	wsa_wrdma_channel_start;
 
 	/* SOC specific initialization like clocks */
 	int (*init)(struct platform_device *pdev);

-- 
2.34.1


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

* [PATCH RFC 10/23] ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (8 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 09/23] ASoC: qcom: lpass: add support for optional WSA interface Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:07   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 11/23] ASoC: qcom: lpass: prepare to handle multiple lpaifs for i2s Neil Armstrong
                   ` (12 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Add the plumbing for the WSA interface in the lpass codec
DMA driver.

The WSA interface uses the same LPM buffer as RXTX and
need to enable more LPM codec mem clocks.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cdc-dma.c | 44 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
index 2dc8c75c4bf0..903055e3188d 100644
--- a/sound/soc/qcom/lpass-cdc-dma.c
+++ b/sound/soc/qcom/lpass-cdc-dma.c
@@ -54,6 +54,14 @@ static void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struc
 		*dmactl = drvdata->va_wr_dmactl;
 		*id = pcm_data->dma_ch - v->va_wrdma_channel_start;
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		*dmactl = drvdata->wsa_rd_dmactl;
+		*id = pcm_data->dma_ch;
+		break;
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		*dmactl = drvdata->wsa_wr_dmactl;
+		*id = pcm_data->dma_ch - v->wsa_wrdma_channel_start;
+		break;
 	default:
 		dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id);
 		break;
@@ -68,49 +76,67 @@ static int __lpass_get_codec_dma_intf_type(int dai_id)
 	case LPASS_CDC_DMA_RX0:
 	case LPASS_CDC_DMA_TX0:
 	case LPASS_CDC_DMA_VA_TX0:
+	case LPASS_CDC_DMA_WSA_TX0:
+	case LPASS_CDC_DMA_WSA_RX0:
 		ret = LPASS_CDC_DMA_INTERFACE1;
 		break;
 	case LPASS_CDC_DMA_RX1:
 	case LPASS_CDC_DMA_TX1:
 	case LPASS_CDC_DMA_VA_TX1:
+	case LPASS_CDC_DMA_WSA_RX1:
+	case LPASS_CDC_DMA_WSA_TX1:
 		ret = LPASS_CDC_DMA_INTERFACE2;
 		break;
 	case LPASS_CDC_DMA_RX2:
 	case LPASS_CDC_DMA_TX2:
 	case LPASS_CDC_DMA_VA_TX2:
+	case LPASS_CDC_DMA_WSA_RX2:
+	case LPASS_CDC_DMA_WSA_TX2:
 		ret = LPASS_CDC_DMA_INTERFACE3;
 		break;
 	case LPASS_CDC_DMA_RX3:
 	case LPASS_CDC_DMA_TX3:
 	case LPASS_CDC_DMA_VA_TX3:
+	case LPASS_CDC_DMA_WSA_RX3:
+	case LPASS_CDC_DMA_WSA_TX3:
 		ret = LPASS_CDC_DMA_INTERFACE4;
 		break;
 	case LPASS_CDC_DMA_RX4:
 	case LPASS_CDC_DMA_TX4:
 	case LPASS_CDC_DMA_VA_TX4:
+	case LPASS_CDC_DMA_WSA_RX4:
 		ret = LPASS_CDC_DMA_INTERFACE5;
 		break;
 	case LPASS_CDC_DMA_RX5:
 	case LPASS_CDC_DMA_TX5:
 	case LPASS_CDC_DMA_VA_TX5:
+	case LPASS_CDC_DMA_WSA_RX5:
+	case LPASS_CDC_DMA_WSA_TX5:
 		ret = LPASS_CDC_DMA_INTERFACE6;
 		break;
 	case LPASS_CDC_DMA_RX6:
 	case LPASS_CDC_DMA_TX6:
 	case LPASS_CDC_DMA_VA_TX6:
+	case LPASS_CDC_DMA_WSA_RX6:
+	case LPASS_CDC_DMA_WSA_TX6:
 		ret = LPASS_CDC_DMA_INTERFACE7;
 		break;
 	case LPASS_CDC_DMA_RX7:
 	case LPASS_CDC_DMA_TX7:
 	case LPASS_CDC_DMA_VA_TX7:
+	case LPASS_CDC_DMA_WSA_RX7:
+	case LPASS_CDC_DMA_WSA_TX7:
 		ret = LPASS_CDC_DMA_INTERFACE8;
 		break;
 	case LPASS_CDC_DMA_RX8:
 	case LPASS_CDC_DMA_TX8:
 	case LPASS_CDC_DMA_VA_TX8:
+	case LPASS_CDC_DMA_WSA_RX8:
+	case LPASS_CDC_DMA_WSA_TX8:
 		ret = LPASS_CDC_DMA_INTERFACE9;
 		break;
 	case LPASS_CDC_DMA_RX9:
+	case LPASS_CDC_DMA_WSA_RX9:
 		ret  = LPASS_CDC_DMA_INTERFACE10;
 		break;
 	default:
@@ -184,6 +210,17 @@ static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
 		clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL);
 		clk_prepare_enable(drvdata->va_mem0);
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);	//??
+		clk_set_rate(drvdata->codec_mem1, CODEC_MEM_HZ_NORMAL);	//??
+		clk_set_rate(drvdata->codec_mem2, CODEC_MEM_HZ_NORMAL);	//??
+		clk_set_rate(drvdata->codec_mem3, CODEC_MEM_HZ_NORMAL);	//??
+		clk_prepare_enable(drvdata->codec_mem0);		//??
+		clk_prepare_enable(drvdata->codec_mem1);		//??
+		clk_prepare_enable(drvdata->codec_mem2);		//??
+		clk_prepare_enable(drvdata->codec_mem3);		//??
+		break;
 	default:
 		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
 		break;
@@ -205,6 +242,13 @@ static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream,
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
 		clk_disable_unprepare(drvdata->va_mem0);
 		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		clk_disable_unprepare(drvdata->codec_mem3);
+		clk_disable_unprepare(drvdata->codec_mem2);
+		clk_disable_unprepare(drvdata->codec_mem1);
+		clk_disable_unprepare(drvdata->codec_mem0);
+		break;
 	default:
 		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
 		break;

-- 
2.34.1


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

* [PATCH RFC 11/23] ASoC: qcom: lpass: prepare to handle multiple lpaifs for i2s
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (9 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 10/23] ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 12/23] ASoC: qcom: lpass: add support for MI2S_SENARY & MI2S_SEPTENARY Neil Armstrong
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

The MI2S ports are spread amonst the different LPAIFs, but today
we consider all 8 possible MI2S ports are available on the core
LPAIF.

In order to support more complex mappings, start by moving the
lpaif struct into a table so we can probe more LPAIFs for
MI2S ports later on.

And since the MI2S_SENARY & MI2S_SEPTENARY indexes are not
following the Quinary index, we need to add an helper to
map the DAI id to the actual MI2S port index.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-apq8016.c  |   9 +-
 sound/soc/qcom/lpass-cpu.c      | 217 +++++++++++++++++++++++-----------------
 sound/soc/qcom/lpass-platform.c | 123 ++++++++++++++---------
 sound/soc/qcom/lpass-sc7180.c   |  10 +-
 sound/soc/qcom/lpass-sc7280.c   |  10 +-
 sound/soc/qcom/lpass.h          |  59 +++++++++--
 6 files changed, 271 insertions(+), 157 deletions(-)

diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index b8f23414eb77..e29db42e0ebb 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -124,16 +124,17 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
 					   int direction, unsigned int dai_id)
 {
 	const struct lpass_variant *v = drvdata->variant;
+	unsigned long *ch_map = &drvdata->lpaif[0].dma_ch_bit_map;
 	int chan = 0;
 
 	if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
-		chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
+		chan = find_first_zero_bit(ch_map,
 					v->rdma_channels);
 
 		if (chan >= v->rdma_channels)
 			return -EBUSY;
 	} else {
-		chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+		chan = find_next_zero_bit(ch_map,
 					v->wrdma_channel_start +
 					v->wrdma_channels,
 					v->wrdma_channel_start);
@@ -142,14 +143,14 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
 			return -EBUSY;
 	}
 
-	set_bit(chan, &drvdata->dma_ch_bit_map);
+	set_bit(chan, ch_map);
 
 	return chan;
 }
 
 static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
 {
-	clear_bit(chan, &drvdata->dma_ch_bit_map);
+	clear_bit(chan, &drvdata->lpaif[0].dma_ch_bit_map);
 
 	return 0;
 }
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index d46b72c926b2..f8b9b2ec9055 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -70,9 +70,11 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 		unsigned int freq, int dir)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
+
 	int ret;
 
-	ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
+	ret = clk_set_rate(drvdata->mi2s_osr_clk[port_id], freq);
 	if (ret)
 		dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n",
 			freq, ret);
@@ -84,17 +86,18 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
 	int ret;
 
-	ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]);
+	ret = clk_prepare_enable(drvdata->mi2s_osr_clk[port_id]);
 	if (ret) {
 		dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);
 		return ret;
 	}
-	ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+	ret = clk_prepare(drvdata->mi2s_bit_clk[port_id]);
 	if (ret) {
 		dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
-		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
+		clk_disable_unprepare(drvdata->mi2s_osr_clk[port_id]);
 		return ret;
 	}
 	return 0;
@@ -104,39 +107,41 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
-	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
-	unsigned int id = dai->driver->id;
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
+	struct lpass_lpaif *lpaif = drvdata->mi2s_lpaif[port_id];
+	unsigned int intf = drvdata->mi2s_intf[port_id];
 
-	clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
+	clk_disable_unprepare(drvdata->mi2s_osr_clk[port_id]);
 	/*
 	 * Ensure LRCLK is disabled even in device node validation.
 	 * Will not impact if disabled in lpass_cpu_daiops_trigger()
 	 * suspend.
 	 */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE);
+		regmap_fields_write(lpaif->i2sctl->spken, intf, LPAIF_I2SCTL_SPKEN_DISABLE);
 	else
-		regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE);
+		regmap_fields_write(lpaif->i2sctl->micen, intf, LPAIF_I2SCTL_MICEN_DISABLE);
 
 	/*
 	 * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before
 	 * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in
 	 * lpass_cpu_daiops_prepare.
 	 */
-	if (drvdata->mi2s_was_prepared[dai->driver->id]) {
-		drvdata->mi2s_was_prepared[dai->driver->id] = false;
-		clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
+	if (drvdata->mi2s_was_prepared[port_id]) {
+		drvdata->mi2s_was_prepared[port_id] = false;
+		clk_disable(drvdata->mi2s_bit_clk[port_id]);
 	}
 
-	clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+	clk_unprepare(drvdata->mi2s_bit_clk[port_id]);
 }
 
 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
-	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
-	unsigned int id = dai->driver->id;
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
+	struct lpass_lpaif *lpaif = drvdata->mi2s_lpaif[port_id];
+	unsigned int intf = drvdata->mi2s_intf[port_id];
 	snd_pcm_format_t format = params_format(params);
 	unsigned int channels = params_channels(params);
 	unsigned int rate = params_rate(params);
@@ -150,14 +155,14 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 		return bitwidth;
 	}
 
-	ret = regmap_fields_write(i2sctl->loopback, id,
+	ret = regmap_fields_write(lpaif->i2sctl->loopback, intf,
 				 LPAIF_I2SCTL_LOOPBACK_DISABLE);
 	if (ret) {
 		dev_err(dai->dev, "error updating loopback field: %d\n", ret);
 		return ret;
 	}
 
-	ret = regmap_fields_write(i2sctl->wssrc, id,
+	ret = regmap_fields_write(lpaif->i2sctl->wssrc, intf,
 				 LPAIF_I2SCTL_WSSRC_INTERNAL);
 	if (ret) {
 		dev_err(dai->dev, "error updating wssrc field: %d\n", ret);
@@ -179,16 +184,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
-	ret = regmap_fields_write(i2sctl->bitwidth, id, regval);
+	ret = regmap_fields_write(lpaif->i2sctl->bitwidth, intf, regval);
 	if (ret) {
 		dev_err(dai->dev, "error updating bitwidth field: %d\n", ret);
 		return ret;
 	}
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		mode = drvdata->mi2s_playback_sd_mode[id];
+		mode = drvdata->mi2s_playback_sd_mode[port_id];
 	else
-		mode = drvdata->mi2s_capture_sd_mode[id];
+		mode = drvdata->mi2s_capture_sd_mode[port_id];
 
 	if (!mode) {
 		dev_err(dai->dev, "no line is assigned\n");
@@ -250,7 +255,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		ret = regmap_fields_write(i2sctl->spkmode, id,
+		ret = regmap_fields_write(lpaif->i2sctl->spkmode, intf,
 					 LPAIF_I2SCTL_SPKMODE(mode));
 		if (ret) {
 			dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n",
@@ -258,13 +263,13 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 			return ret;
 		}
 		if (channels >= 2)
-			ret = regmap_fields_write(i2sctl->spkmono, id,
+			ret = regmap_fields_write(lpaif->i2sctl->spkmono, intf,
 						 LPAIF_I2SCTL_SPKMONO_STEREO);
 		else
-			ret = regmap_fields_write(i2sctl->spkmono, id,
+			ret = regmap_fields_write(lpaif->i2sctl->spkmono, intf,
 						 LPAIF_I2SCTL_SPKMONO_MONO);
 	} else {
-		ret = regmap_fields_write(i2sctl->micmode, id,
+		ret = regmap_fields_write(lpaif->i2sctl->micmode, intf,
 					 LPAIF_I2SCTL_MICMODE(mode));
 		if (ret) {
 			dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n",
@@ -272,10 +277,10 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 			return ret;
 		}
 		if (channels >= 2)
-			ret = regmap_fields_write(i2sctl->micmono, id,
+			ret = regmap_fields_write(lpaif->i2sctl->micmono, intf,
 						 LPAIF_I2SCTL_MICMONO_STEREO);
 		else
-			ret = regmap_fields_write(i2sctl->micmono, id,
+			ret = regmap_fields_write(lpaif->i2sctl->micmono, intf,
 						 LPAIF_I2SCTL_MICMONO_MONO);
 	}
 
@@ -285,7 +290,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
-	ret = clk_set_rate(drvdata->mi2s_bit_clk[id],
+	ret = clk_set_rate(drvdata->mi2s_bit_clk[port_id],
 			   rate * bitwidth * 2);
 	if (ret) {
 		dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
@@ -300,8 +305,9 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 		int cmd, struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
-	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
-	unsigned int id = dai->driver->id;
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
+	struct lpass_lpaif *lpaif = drvdata->mi2s_lpaif[port_id];
+	unsigned int intf = drvdata->mi2s_intf[port_id];
 	int ret = -EINVAL;
 
 	switch (cmd) {
@@ -321,20 +327,20 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 		 *     it.
 		 */
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			ret = regmap_fields_write(i2sctl->spken, id,
+			ret = regmap_fields_write(lpaif->i2sctl->spken, intf,
 						 LPAIF_I2SCTL_SPKEN_ENABLE);
 		} else  {
-			ret = regmap_fields_write(i2sctl->micen, id,
+			ret = regmap_fields_write(lpaif->i2sctl->micen, intf,
 						 LPAIF_I2SCTL_MICEN_ENABLE);
 		}
 		if (ret)
 			dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
 				ret);
 
-		ret = clk_enable(drvdata->mi2s_bit_clk[id]);
+		ret = clk_enable(drvdata->mi2s_bit_clk[port_id]);
 		if (ret) {
 			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
-			clk_disable(drvdata->mi2s_osr_clk[id]);
+			clk_disable(drvdata->mi2s_osr_clk[port_id]);
 			return ret;
 		}
 		break;
@@ -346,17 +352,17 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 		 * device suspend.
 		 */
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			ret = regmap_fields_write(i2sctl->spken, id,
+			ret = regmap_fields_write(lpaif->i2sctl->spken, intf,
 						 LPAIF_I2SCTL_SPKEN_DISABLE);
 		} else  {
-			ret = regmap_fields_write(i2sctl->micen, id,
+			ret = regmap_fields_write(lpaif->i2sctl->micen, intf,
 						 LPAIF_I2SCTL_MICEN_DISABLE);
 		}
 		if (ret)
 			dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
 				ret);
 
-		clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
+		clk_disable(drvdata->mi2s_bit_clk[port_id]);
 
 		break;
 	}
@@ -368,8 +374,9 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
-	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
-	unsigned int id = dai->driver->id;
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
+	struct lpass_lpaif *lpaif = drvdata->mi2s_lpaif[port_id];
+	unsigned int intf = drvdata->mi2s_intf[port_id];
 	int ret;
 
 	/*
@@ -379,9 +386,9 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 	 * (ex: to drop start up pop noise before capture starts).
 	 */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE);
+		ret = regmap_fields_write(lpaif->i2sctl->spken, intf, LPAIF_I2SCTL_SPKEN_ENABLE);
 	else
-		ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE);
+		ret = regmap_fields_write(lpaif->i2sctl->micen, intf, LPAIF_I2SCTL_MICEN_ENABLE);
 
 	if (ret) {
 		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
@@ -393,13 +400,13 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 	 * be called multiple times. It's paired with the clk_disable in
 	 * lpass_cpu_daiops_shutdown.
 	 */
-	if (!drvdata->mi2s_was_prepared[dai->driver->id]) {
-		ret = clk_enable(drvdata->mi2s_bit_clk[id]);
+	if (!drvdata->mi2s_was_prepared[port_id]) {
+		ret = clk_enable(drvdata->mi2s_bit_clk[port_id]);
 		if (ret) {
 			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
 			return ret;
 		}
-		drvdata->mi2s_was_prepared[dai->driver->id] = true;
+		drvdata->mi2s_was_prepared[port_id] = true;
 	}
 	return 0;
 }
@@ -409,8 +416,9 @@ static int lpass_cpu_daiops_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_
 	int ret;
 	struct snd_soc_dai_driver *drv = dai->driver;
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	unsigned int port_id = mi2s_port_id(dai->id);
 
-	if (drvdata->mi2s_playback_sd_mode[dai->id] == LPAIF_I2SCTL_MODE_QUAD01) {
+	if (drvdata->mi2s_playback_sd_mode[port_id] == LPAIF_I2SCTL_MODE_QUAD01) {
 		ret =  snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
 				lpass_quad_chmaps, drv->playback.channels_max, 0,
 				NULL);
@@ -424,11 +432,13 @@ static int lpass_cpu_daiops_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_
 static int lpass_cpu_daiops_probe(struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	unsigned int port_id = mi2s_port_id(dai->driver->id);
+	struct lpass_lpaif *lpaif = drvdata->mi2s_lpaif[port_id];
 	int ret;
 
 	/* ensure audio hardware is disabled */
-	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
+	ret = regmap_write(lpaif->map,
+			LPAIF_I2SCTL_REG(drvdata->variant, drvdata->mi2s_intf[port_id]), 0);
 	if (ret)
 		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);
 
@@ -578,7 +588,7 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
 	return false;
 }
 
-static struct regmap_config lpass_cpu_regmap_config = {
+static const struct regmap_config lpass_cpu_regmap_config = {
 	.name = "lpass_cpu",
 	.reg_bits = 32,
 	.reg_stride = 4,
@@ -1040,9 +1050,12 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
 
 	/* Allow all channels by default for backwards compatibility */
 	for (i = 0; i < data->variant->num_dai; i++) {
-		id = data->variant->dai_driver[i].id;
-		data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
-		data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+		if (is_mi2s_port_id(data->variant->dai_driver[i].id)) {
+			unsigned int port_id = mi2s_port_id(data->variant->dai_driver[i].id);
+
+			data->mi2s_playback_sd_mode[port_id] = LPAIF_I2SCTL_MODE_8CH;
+			data->mi2s_capture_sd_mode[port_id] = LPAIF_I2SCTL_MODE_8CH;
+		}
 	}
 
 	for_each_child_of_node(dev->of_node, node) {
@@ -1057,13 +1070,18 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
 			data->codec_dma_enable = 1;
 			if (is_wsa_cdc_dma_port(id))
 				data->wsa_enable = 1;
-		} else {
-			data->mi2s_playback_sd_mode[id] =
+		} else if (is_mi2s_port_id(id)) {
+			unsigned int port_id = mi2s_port_id(id);
+
+			data->mi2s_playback_sd_mode[port_id] =
 				of_lpass_cpu_parse_sd_lines(dev, node,
 							    "qcom,playback-sd-lines");
-			data->mi2s_capture_sd_mode[id] =
+			data->mi2s_capture_sd_mode[port_id] =
 				of_lpass_cpu_parse_sd_lines(dev, node,
 						    "qcom,capture-sd-lines");
+		} else {
+			dev_err(dev, "invalid dai id: %d\n", id);
+			continue;
 		}
 	}
 }
@@ -1094,6 +1112,45 @@ static int of_lpass_cdc_dma_clks_parse(struct device *dev,
 	return 0;
 }
 
+static int lpass_cpu_init_lpaif(struct platform_device *pdev, int idx,
+				const char *reg_name)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	const struct lpass_variant *v = drvdata->variant;
+	struct lpass_lpaif *lpaif = &drvdata->lpaif[idx];
+	struct regmap_config cfg = lpass_cpu_regmap_config;
+	struct device *dev = &pdev->dev;
+	void __iomem *base;
+	int ret;
+
+	base = devm_platform_ioremap_resource_byname(pdev, reg_name);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	cfg.max_register = LPAIF_WRDMAPER_REG(v, v->wrdma_channels +
+					      v->wrdma_channel_start);
+
+	lpaif->drvdata = drvdata;
+	lpaif->map = devm_regmap_init_mmio(dev, base, &cfg);
+	if (IS_ERR(lpaif->map)) {
+		dev_err(dev, "error initializing %s regmap: %ld\n",
+			reg_name, PTR_ERR(lpaif->map));
+		return PTR_ERR(lpaif->map);
+	}
+
+	/* Allocation for i2sctl regmap fields */
+	lpaif->i2sctl = devm_kzalloc(dev, sizeof(*lpaif->i2sctl), GFP_KERNEL);
+	if (!lpaif->i2sctl)
+		return -ENOMEM;
+
+	/* Initialize bitfields for dai I2SCTL register */
+	ret = lpass_cpu_init_i2sctl_bitfields(dev, lpaif->i2sctl, lpaif->map);
+	if (ret)
+		dev_err(dev, "error init %s i2sctl field: %d\n", reg_name, ret);
+
+	return ret;
+}
+
 int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata;
@@ -1101,7 +1158,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 	struct resource *res;
 	const struct lpass_variant *variant;
 	struct device *dev = &pdev->dev;
-	int ret, i, dai_id;
+	int ret, i, port_id;
 
 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
 	if (dsp_of_node) {
@@ -1190,21 +1247,11 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 		drvdata->va_cdc_dma_lpm_buf = res->start;
 	}
 
-	drvdata->lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-lpaif");
-	if (IS_ERR(drvdata->lpaif))
-		return PTR_ERR(drvdata->lpaif);
-
-	lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
-						variant->wrdma_channels +
-						variant->wrdma_channel_start);
+	drvdata->num_lpaif = 1;
 
-	drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif,
-			&lpass_cpu_regmap_config);
-	if (IS_ERR(drvdata->lpaif_map)) {
-		dev_err(dev, "error initializing regmap: %ld\n",
-			PTR_ERR(drvdata->lpaif_map));
-		return PTR_ERR(drvdata->lpaif_map);
-	}
+	ret = lpass_cpu_init_lpaif(pdev, 0, "lpass-lpaif");
+	if (ret)
+		return ret;
 
 	if (drvdata->hdmi_port_enable) {
 		drvdata->hdmiif = devm_platform_ioremap_resource_byname(pdev, "lpass-hdmiif");
@@ -1231,42 +1278,32 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 	}
 
 	for (i = 0; i < variant->num_dai; i++) {
-		dai_id = variant->dai_driver[i].id;
-		if (dai_id == LPASS_DP_RX || is_cdc_dma_port(dai_id))
+		if (!is_mi2s_port_id(variant->dai_driver[i].id))
 			continue;
 
-		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev,
+		port_id = mi2s_port_id(variant->dai_driver[i].id);
+
+		drvdata->mi2s_lpaif[port_id] = &drvdata->lpaif[0];
+		drvdata->mi2s_intf[port_id] = port_id;
+
+		drvdata->mi2s_osr_clk[port_id] = devm_clk_get_optional(dev,
 					     variant->dai_osr_clk_names[i]);
-		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(dev,
+		drvdata->mi2s_bit_clk[port_id] = devm_clk_get(dev,
 						variant->dai_bit_clk_names[i]);
-		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
+		if (IS_ERR(drvdata->mi2s_bit_clk[port_id])) {
 			dev_err(dev,
 				"error getting %s: %ld\n",
 				variant->dai_bit_clk_names[i],
-				PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
-			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
+				PTR_ERR(drvdata->mi2s_bit_clk[port_id]));
+			return PTR_ERR(drvdata->mi2s_bit_clk[port_id]);
 		}
-		if (drvdata->mi2s_playback_sd_mode[dai_id] ==
+		if (drvdata->mi2s_playback_sd_mode[port_id] ==
 			LPAIF_I2SCTL_MODE_QUAD01) {
 			variant->dai_driver[i].playback.channels_min = 4;
 			variant->dai_driver[i].playback.channels_max = 4;
 		}
 	}
 
-	/* Allocation for i2sctl regmap fields */
-	drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl),
-					GFP_KERNEL);
-	if (!drvdata->i2sctl)
-		return -ENOMEM;
-
-	/* Initialize bitfields for dai I2SCTL register */
-	ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl,
-						drvdata->lpaif_map);
-	if (ret) {
-		dev_err(dev, "error init i2sctl field: %d\n", ret);
-		return ret;
-	}
-
 	if (drvdata->hdmi_port_enable) {
 		ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map);
 		if (ret) {
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 9c879a8090bb..574b46e2176d 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -194,25 +194,26 @@ static int lpass_platform_alloc_wsa_dmactl_fields(struct device *dev,
 }
 
 static int lpass_platform_alloc_dmactl_fields(struct device *dev,
-					 struct regmap *map)
+					      struct lpass_lpaif *lpaif)
 {
 	struct lpass_data *drvdata = dev_get_drvdata(dev);
 	const struct lpass_variant *v = drvdata->variant;
+	struct regmap *map = lpaif->map;
 	struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
 	int rval;
 
-	drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
-					  GFP_KERNEL);
-	if (drvdata->rd_dmactl == NULL)
+	lpaif->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
+					GFP_KERNEL);
+	if (!lpaif->rd_dmactl)
 		return -ENOMEM;
 
-	drvdata->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
-					  GFP_KERNEL);
-	if (drvdata->wr_dmactl == NULL)
+	lpaif->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl),
+					GFP_KERNEL);
+	if (!lpaif->wr_dmactl)
 		return -ENOMEM;
 
-	rd_dmactl = drvdata->rd_dmactl;
-	wr_dmactl = drvdata->wr_dmactl;
+	rd_dmactl = lpaif->rd_dmactl;
+	wr_dmactl = lpaif->wr_dmactl;
 
 	rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf,
 					    &v->rdma_intf, 6);
@@ -252,6 +253,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 	struct lpass_pcm_data *data;
 	struct regmap *map;
 	unsigned int dai_id = cpu_dai->driver->id;
+	unsigned int port_id;
 
 	data = kzalloc_obj(*data);
 	if (!data)
@@ -272,8 +274,9 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
-		map = drvdata->lpaif_map;
-		drvdata->substream[dma_ch] = substream;
+		port_id = mi2s_port_id(dai_id);
+		map = drvdata->mi2s_lpaif[port_id]->map;
+		drvdata->mi2s_lpaif[port_id]->substream[dma_ch] = substream;
 		break;
 	case LPASS_DP_RX:
 		map = drvdata->hdmiif_map;
@@ -352,12 +355,14 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component,
 	const struct lpass_variant *v = drvdata->variant;
 	struct lpass_pcm_data *data;
 	unsigned int dai_id = cpu_dai->driver->id;
+	unsigned int port_id;
 
 	data = runtime->private_data;
 
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
-		drvdata->substream[data->dma_ch] = NULL;
+		port_id = mi2s_port_id(dai_id);
+		drvdata->mi2s_lpaif[port_id]->substream[data->dma_ch] = NULL;
 		break;
 	case LPASS_DP_RX:
 		drvdata->hdmi_substream[data->dma_ch] = NULL;
@@ -391,13 +396,15 @@ static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_subst
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
 	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	struct lpaif_dmactl *dmactl = NULL;
+	unsigned int port_id;
 
 	switch (cpu_dai->driver->id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+		port_id = mi2s_port_id(cpu_dai->driver->id);
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			dmactl = drvdata->rd_dmactl;
+			dmactl = drvdata->mi2s_lpaif[port_id]->rd_dmactl;
 		else
-			dmactl = drvdata->wr_dmactl;
+			dmactl = drvdata->mi2s_lpaif[port_id]->wr_dmactl;
 		break;
 	case LPASS_DP_RX:
 		dmactl = drvdata->hdmi_rd_dmactl;
@@ -470,10 +477,12 @@ static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream *
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
 	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	struct regmap *map = NULL;
+	unsigned int port_id;
 
 	switch (cpu_dai->driver->id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
-		map = drvdata->lpaif_map;
+		port_id = mi2s_port_id(cpu_dai->driver->id);
+		map = drvdata->mi2s_lpaif[port_id]->map;
 		break;
 	case LPASS_DP_RX:
 		map = drvdata->hdmiif_map;
@@ -501,8 +510,6 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
 	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
-	struct snd_pcm_runtime *rt = substream->runtime;
-	struct lpass_pcm_data *pcm_data = rt->private_data;
 	const struct lpass_variant *v = drvdata->variant;
 	snd_pcm_format_t format = params_format(params);
 	unsigned int channels = params_channels(params);
@@ -510,8 +517,8 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 	struct lpaif_dmactl *dmactl;
 	int id;
 	int bitwidth;
-	int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
 	unsigned int dai_id = cpu_dai->driver->id;
+	int ret, dma_port;
 
 	dmactl = __lpass_get_dmactl_handle(substream, component);
 	id = __lpass_get_id(substream, component);
@@ -561,6 +568,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 	case MI2S_TERTIARY:
 	case MI2S_QUATERNARY:
 	case MI2S_QUINARY:
+		dma_port = drvdata->mi2s_intf[dai_id] + v->dmactl_audif_start;
 		ret = regmap_fields_write(dmactl->intf, id,
 						LPAIF_DMACTL_AUDINTF(dma_port));
 		if (ret) {
@@ -1049,6 +1057,7 @@ static irqreturn_t lpass_dma_interrupt_handler(
 	unsigned int reg, val, mask;
 	struct regmap *map;
 	unsigned int dai_id = cpu_dai->driver->id;
+	unsigned int port_id;
 
 	mask = LPAIF_IRQ_ALL(chan);
 	switch (dai_id) {
@@ -1064,7 +1073,8 @@ static irqreturn_t lpass_dma_interrupt_handler(
 	case MI2S_TERTIARY:
 	case MI2S_QUATERNARY:
 	case MI2S_QUINARY:
-		map = drvdata->lpaif_map;
+		port_id = mi2s_port_id(dai_id);
+		map = drvdata->mi2s_lpaif[port_id]->map;
 		reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 		val = 0;
 	break;
@@ -1140,12 +1150,13 @@ static irqreturn_t lpass_dma_interrupt_handler(
 
 static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 {
-	struct lpass_data *drvdata = data;
+	struct lpass_lpaif *lpaif = data;
+	struct lpass_data *drvdata = lpaif->drvdata;
 	const struct lpass_variant *v = drvdata->variant;
 	unsigned int irqs;
 	int rv, chan;
 
-	rv = regmap_read(drvdata->lpaif_map,
+	rv = regmap_read(lpaif->map,
 			LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
 	if (rv) {
 		pr_err("error reading from irqstat reg: %d\n", rv);
@@ -1154,9 +1165,9 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 
 	/* Handle per channel interrupts */
 	for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
-		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
+		if (irqs & LPAIF_IRQ_ALL(chan) && lpaif->substream[chan]) {
 			rv = lpass_dma_interrupt_handler(
-						drvdata->substream[chan],
+						lpaif->substream[chan],
 						drvdata, chan, irqs);
 			if (rv != IRQ_HANDLED)
 				return rv;
@@ -1343,6 +1354,7 @@ static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
 {
 	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	struct regmap *map;
+	int i;
 
 	if (drvdata->hdmi_port_enable) {
 		map = drvdata->hdmiif_map;
@@ -1350,9 +1362,11 @@ static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
 		regcache_mark_dirty(map);
 	}
 
-	map = drvdata->lpaif_map;
-	regcache_cache_only(map, true);
-	regcache_mark_dirty(map);
+	for (i = 0; i < drvdata->num_lpaif; i++) {
+		map = drvdata->lpaif[i].map;
+		regcache_cache_only(map, true);
+		regcache_mark_dirty(map);
+	}
 
 	return 0;
 }
@@ -1361,7 +1375,7 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
 {
 	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	struct regmap *map;
-	int ret;
+	int i, ret;
 
 	if (drvdata->hdmi_port_enable) {
 		map = drvdata->hdmiif_map;
@@ -1371,10 +1385,15 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
 			return ret;
 	}
 
-	map = drvdata->lpaif_map;
-	regcache_cache_only(map, false);
+	for (i = 0; i < drvdata->num_lpaif; i++) {
+		map = drvdata->lpaif[i].map;
+		regcache_cache_only(map, false);
+		ret = regcache_sync(map);
+		if (ret)
+			return ret;
+	}
 
-	return regcache_sync(map);
+	return 0;
 }
 
 static int lpass_platform_copy(struct snd_soc_component *component,
@@ -1429,39 +1448,51 @@ static const struct snd_soc_component_driver lpass_component_driver = {
 
 };
 
-int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
+static int lpass_platform_init_lpaif(struct platform_device *pdev, int idx,
+				     const char *irq_name, unsigned long irqflags)
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct lpass_lpaif *lpaif = &drvdata->lpaif[idx];
 	const struct lpass_variant *v = drvdata->variant;
 	int ret;
 
-	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
-	if (drvdata->lpaif_irq < 0)
+	lpaif->irq = platform_get_irq_byname(pdev, irq_name);
+	if (lpaif->irq < 0)
 		return -ENODEV;
 
 	/* ensure audio hardware is disabled */
-	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
+	ret = regmap_write(lpaif->map, LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
 	if (ret) {
-		dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
+		dev_err(&pdev->dev, "error writing to %s irqen reg: %d\n",
+			irq_name, ret);
 		return ret;
 	}
 
-	ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
-			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
-			"lpass-irq-lpaif", drvdata);
+	ret = devm_request_irq(&pdev->dev, lpaif->irq, lpass_platform_lpaif_irq,
+			       irqflags, irq_name, lpaif);
 	if (ret) {
-		dev_err(&pdev->dev, "irq request failed: %d\n", ret);
+		dev_err(&pdev->dev, "%s irq request failed: %d\n", irq_name, ret);
 		return ret;
 	}
 
-	ret = lpass_platform_alloc_dmactl_fields(&pdev->dev,
-						 drvdata->lpaif_map);
-	if (ret) {
-		dev_err(&pdev->dev,
-			"error initializing dmactl fields: %d\n", ret);
+	ret = lpass_platform_alloc_dmactl_fields(&pdev->dev, lpaif);
+	if (ret)
+		dev_err(&pdev->dev, "error initializing %s dmactl fields: %d\n",
+			irq_name, ret);
+
+	return ret;
+}
+
+int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	const struct lpass_variant *v = drvdata->variant;
+	int ret;
+
+	ret = lpass_platform_init_lpaif(pdev, 0, "lpass-irq-lpaif",
+			IRQF_TRIGGER_RISING);
+	if (ret)
 		return ret;
-	}
 
 	if (drvdata->codec_dma_enable) {
 		ret = regmap_write(drvdata->rxtx_lpaif_map,
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index 7c6a9b0fda89..d3ff080e1aa0 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -89,14 +89,16 @@ static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata,
 		}
 		set_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
 	} else {
+		unsigned long *ch_map = &drvdata->lpaif[0].dma_ch_bit_map;
+
 		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
-			chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
+			chan = find_first_zero_bit(ch_map,
 						v->rdma_channels);
 
 			if (chan >= v->rdma_channels)
 				return -EBUSY;
 		} else {
-			chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+			chan = find_next_zero_bit(ch_map,
 					v->wrdma_channel_start +
 					v->wrdma_channels,
 					v->wrdma_channel_start);
@@ -105,7 +107,7 @@ static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata,
 				return -EBUSY;
 		}
 
-		set_bit(chan, &drvdata->dma_ch_bit_map);
+		set_bit(chan, ch_map);
 	}
 	return chan;
 }
@@ -115,7 +117,7 @@ static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, u
 	if (dai_id == LPASS_DP_RX)
 		clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
 	else
-		clear_bit(chan, &drvdata->dma_ch_bit_map);
+		clear_bit(chan, &drvdata->lpaif[0].dma_ch_bit_map);
 
 	return 0;
 }
diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c
index 817c824f9179..afa9f9e9ba3c 100644
--- a/sound/soc/qcom/lpass-sc7280.c
+++ b/sound/soc/qcom/lpass-sc7280.c
@@ -111,18 +111,20 @@ static int sc7280_lpass_alloc_dma_channel(struct lpass_data *drvdata,
 					  int direction, unsigned int dai_id)
 {
 	const struct lpass_variant *v = drvdata->variant;
+	unsigned long *ch_map;
 	int chan = 0;
 
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+		ch_map = &drvdata->lpaif[0].dma_ch_bit_map;
 		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
-			chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
+			chan = find_first_zero_bit(ch_map,
 						   v->rdma_channels);
 
 			if (chan >= v->rdma_channels)
 				return -EBUSY;
 		} else {
-			chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+			chan = find_next_zero_bit(ch_map,
 						  v->wrdma_channel_start +
 						  v->wrdma_channels,
 						  v->wrdma_channel_start);
@@ -130,7 +132,7 @@ static int sc7280_lpass_alloc_dma_channel(struct lpass_data *drvdata,
 			if (chan >= v->wrdma_channel_start + v->wrdma_channels)
 				return -EBUSY;
 		}
-		set_bit(chan, &drvdata->dma_ch_bit_map);
+		set_bit(chan, ch_map);
 		break;
 	case LPASS_DP_RX:
 		chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map,
@@ -174,7 +176,7 @@ static int sc7280_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, u
 {
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
-		clear_bit(chan, &drvdata->dma_ch_bit_map);
+		clear_bit(chan, &drvdata->lpaif[0].dma_ch_bit_map);
 		break;
 	case LPASS_DP_RX:
 		clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 46ae02e67403..78125048b064 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -20,6 +20,7 @@
 #define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
 #define LPASS_MAX_PORTS			(LPASS_MAX_PORT)
 #define LPASS_MAX_MI2S_PORTS			(8)
+#define LPASS_MAX_LPAIF				(1)
 #define LPASS_MAX_DMA_CHANNELS			(8)
 #define LPASS_MAX_HDMI_DMA_CHANNELS		(4)
 #define LPASS_MAX_CDC_DMA_CHANNELS		(8)
@@ -40,6 +41,26 @@
 			return -EINVAL;         \
 	} while (0)
 
+static inline bool is_mi2s_port_id(int dai_id)
+{
+	switch (dai_id) {
+	case MI2S_PRIMARY ... MI2S_QUINARY:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline int mi2s_port_id(int dai_id)
+{
+	switch (dai_id) {
+	case MI2S_PRIMARY ... MI2S_QUINARY:
+		return dai_id;
+	default:
+		return 0;
+	}
+}
+
 static inline bool is_cdc_dma_port(int dai_id)
 {
 	switch (dai_id) {
@@ -104,6 +125,26 @@ struct lpaif_dmactl {
 	struct regmap_field *codec_fs_delay;
 };
 
+struct lpass_data;
+
+/* LPAIF instance hosting MI2S interfaces with their own DMAs and interrupt */
+struct lpass_lpaif {
+	struct lpass_data *drvdata;
+	struct regmap *map;
+	int irq;
+
+	/* Regmap fields of I2SCTL & DMACTL registers bitfields */
+	struct lpaif_i2sctl *i2sctl;
+	struct lpaif_dmactl *rd_dmactl;
+	struct lpaif_dmactl *wr_dmactl;
+
+	/* bit map to keep track of static channel allocations */
+	unsigned long dma_ch_bit_map;
+
+	/* used it for handling interrupt per dma channel */
+	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
+};
+
 /* Both the CPU DAI and platform drivers will access this data */
 struct lpass_data {
 
@@ -133,8 +174,15 @@ struct lpass_data {
 	int codec_dma_enable;
 	int wsa_enable;
 
+	/* LPAIF instances, 0 is the core "lpass-lpaif" one */
+	struct lpass_lpaif lpaif[LPASS_MAX_LPAIF];
+	int num_lpaif;
+
+	/* LPAIF instance and interface index of each MI2S port */
+	struct lpass_lpaif *mi2s_lpaif[LPASS_MAX_MI2S_PORTS];
+	unsigned int mi2s_intf[LPASS_MAX_MI2S_PORTS];
+
 	/* low-power audio interface (LPAIF) registers */
-	void __iomem *lpaif;
 	void __iomem *hdmiif;
 	void __iomem *rxtx_lpaif;
 	void __iomem *va_lpaif;
@@ -144,14 +192,12 @@ struct lpass_data {
 	u32 va_cdc_dma_lpm_buf;
 
 	/* regmap backed by the low-power audio interface (LPAIF) registers */
-	struct regmap *lpaif_map;
 	struct regmap *hdmiif_map;
 	struct regmap *rxtx_lpaif_map;
 	struct regmap *va_lpaif_map;
 	struct regmap *wsa_lpaif_map;
 
 	/* interrupts from the low-power audio interface (LPAIF) */
-	int lpaif_irq;
 	int hdmiif_irq;
 	int rxtxif_irq;
 	int vaif_irq;
@@ -161,14 +207,12 @@ struct lpass_data {
 	const struct lpass_variant *variant;
 
 	/* bit map to keep track of static channel allocations */
-	unsigned long dma_ch_bit_map;
 	unsigned long hdmi_dma_ch_bit_map;
 	unsigned long rxtx_dma_ch_bit_map;
 	unsigned long va_dma_ch_bit_map;
 	unsigned long wsa_dma_ch_bit_map;
 
 	/* used it for handling interrupt per dma channel */
-	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
 	struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS];
 	struct snd_pcm_substream *rxtx_substream[LPASS_MAX_CDC_DMA_CHANNELS];
 	struct snd_pcm_substream *va_substream[LPASS_MAX_CDC_DMA_CHANNELS];
@@ -178,10 +222,7 @@ struct lpass_data {
 	struct clk_bulk_data *clks;
 	int num_clks;
 
-	/* Regmap fields of I2SCTL & DMACTL registers bitfields */
-	struct lpaif_i2sctl *i2sctl;
-	struct lpaif_dmactl *rd_dmactl;
-	struct lpaif_dmactl *wr_dmactl;
+	/* Regmap fields of HDMI DMACTL registers bitfields */
 	struct lpaif_dmactl *hdmi_rd_dmactl;
 
 	/* Regmap fields of CODEC DMA CTRL registers */

-- 
2.34.1


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

* [PATCH RFC 12/23] ASoC: qcom: lpass: add support for MI2S_SENARY & MI2S_SEPTENARY
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (10 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 11/23] ASoC: qcom: lpass: prepare to handle multiple lpaifs for i2s Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 13/23] ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs Neil Armstrong
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Now we use the helper to translate the DAI id into a proper
MI2S port index, we can add support for the MI2S_SENARY &
MI2S_SEPTENARY ports.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-platform.c | 14 +++++++++++++-
 sound/soc/qcom/lpass.h          |  3 +++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 574b46e2176d..2400a6d16bb8 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -274,6 +274,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		port_id = mi2s_port_id(dai_id);
 		map = drvdata->mi2s_lpaif[port_id]->map;
 		drvdata->mi2s_lpaif[port_id]->substream[dma_ch] = substream;
@@ -303,6 +304,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 	data->dma_ch = dma_ch;
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 	case LPASS_DP_RX:
 		ret = regmap_write(map, LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0);
 		if (ret) {
@@ -361,6 +363,7 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component,
 
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		port_id = mi2s_port_id(dai_id);
 		drvdata->mi2s_lpaif[port_id]->substream[data->dma_ch] = NULL;
 		break;
@@ -400,6 +403,7 @@ static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_subst
 
 	switch (cpu_dai->driver->id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		port_id = mi2s_port_id(cpu_dai->driver->id);
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 			dmactl = drvdata->mi2s_lpaif[port_id]->rd_dmactl;
@@ -442,6 +446,7 @@ static int __lpass_get_id(const struct snd_pcm_substream *substream,
 
 	switch (cpu_dai->driver->id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 			id = pcm_data->dma_ch;
 		else
@@ -481,6 +486,7 @@ static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream *
 
 	switch (cpu_dai->driver->id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		port_id = mi2s_port_id(cpu_dai->driver->id);
 		map = drvdata->mi2s_lpaif[port_id]->map;
 		break;
@@ -518,6 +524,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 	int id;
 	int bitwidth;
 	unsigned int dai_id = cpu_dai->driver->id;
+	unsigned int port_id;
 	int ret, dma_port;
 
 	dmactl = __lpass_get_dmactl_handle(substream, component);
@@ -568,7 +575,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
 	case MI2S_TERTIARY:
 	case MI2S_QUATERNARY:
 	case MI2S_QUINARY:
-		dma_port = drvdata->mi2s_intf[dai_id] + v->dmactl_audif_start;
+	case MI2S_SENARY ... MI2S_SEPTENARY:
+		port_id = mi2s_port_id(dai_id);
+		dma_port = drvdata->mi2s_intf[port_id] + v->dmactl_audif_start;
 		ret = regmap_fields_write(dmactl->intf, id,
 						LPAIF_DMACTL_AUDINTF(dma_port));
 		if (ret) {
@@ -811,6 +820,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
 		case MI2S_TERTIARY:
 		case MI2S_QUATERNARY:
 		case MI2S_QUINARY:
+		case MI2S_SENARY ... MI2S_SEPTENARY:
 			reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
 			val_irqclr = LPAIF_IRQ_ALL(ch);
 
@@ -910,6 +920,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
 		case MI2S_TERTIARY:
 		case MI2S_QUATERNARY:
 		case MI2S_QUINARY:
+		case MI2S_SENARY ... MI2S_SEPTENARY:
 			reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
 			val_mask = LPAIF_IRQ_ALL(ch);
 			val_irqen = 0;
@@ -1073,6 +1084,7 @@ static irqreturn_t lpass_dma_interrupt_handler(
 	case MI2S_TERTIARY:
 	case MI2S_QUATERNARY:
 	case MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		port_id = mi2s_port_id(dai_id);
 		map = drvdata->mi2s_lpaif[port_id]->map;
 		reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 78125048b064..4407996fb145 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -45,6 +45,7 @@ static inline bool is_mi2s_port_id(int dai_id)
 {
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
 		return true;
 	default:
 		return false;
@@ -56,6 +57,8 @@ static inline int mi2s_port_id(int dai_id)
 	switch (dai_id) {
 	case MI2S_PRIMARY ... MI2S_QUINARY:
 		return dai_id;
+	case MI2S_SENARY ... MI2S_SEPTENARY:
+		return MI2S_QUINARY + (dai_id - MI2S_SENARY) + 1;
 	default:
 		return 0;
 	}

-- 
2.34.1


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

* [PATCH RFC 13/23] ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (11 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 12/23] ASoC: qcom: lpass: add support for MI2S_SENARY & MI2S_SEPTENARY Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:04   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 14/23] ASoC: qcom: lpass: support LPM memory type for I2S LPAIFs Neil Armstrong
                   ` (9 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Now we have all the plumbing to support multiple MI2S ports
on multiple LPAIFs, add the optional descriptor and parse it
to probe the associated LPAIF when defined in the DT node.

When the descriptor is not provided, the behaviour is kept
as before and the 8 MI2S ports are mapped to the core
LPAIF.

Since an LPAIF address space can only be mapped by the MI2S
handling or the VA/RXTX/WSA handlers, if somehow the DT
declared some invalid setup (I2S port on WSA and WSA codec),
the probe would fail.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cpu.c      | 73 +++++++++++++++++++++++++++++++++++++----
 sound/soc/qcom/lpass-platform.c | 27 ++++++++++++---
 sound/soc/qcom/lpass.h          | 23 ++++++++++++-
 3 files changed, 110 insertions(+), 13 deletions(-)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index f8b9b2ec9055..411eecd9cbb9 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1079,6 +1079,8 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
 			data->mi2s_capture_sd_mode[port_id] =
 				of_lpass_cpu_parse_sd_lines(dev, node,
 						    "qcom,capture-sd-lines");
+
+			set_bit(port_id, &data->lpaif_port_enable);
 		} else {
 			dev_err(dev, "invalid dai id: %d\n", id);
 			continue;
@@ -1121,12 +1123,33 @@ static int lpass_cpu_init_lpaif(struct platform_device *pdev, int idx,
 	struct regmap_config cfg = lpass_cpu_regmap_config;
 	struct device *dev = &pdev->dev;
 	void __iomem *base;
-	int ret;
+	int ret, i;
+
+	/* Only probe an LPAIF if one of the port is described in DT */
+	if (v->mi2s_desc) {
+		const struct lpaif_mi2s_desc *desc = &v->mi2s_desc[idx];
+		bool enabled = false;
+
+		for (i = 0; i < desc->port_desc_count; ++i) {
+			unsigned int port_id = mi2s_port_id(desc->port_desc[i].id);
+
+			if (test_bit(port_id, &drvdata->lpaif_port_enable)) {
+				enabled = true;
+				break;
+			}
+		}
+
+		if (!enabled)
+			return 0;
+	}
 
 	base = devm_platform_ioremap_resource_byname(pdev, reg_name);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
 
+	/* Keep the legacy regmap name for the core LPAIF */
+	if (idx)
+		cfg.name = reg_name;
 	cfg.max_register = LPAIF_WRDMAPER_REG(v, v->wrdma_channels +
 					      v->wrdma_channel_start);
 
@@ -1247,11 +1270,25 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 		drvdata->va_cdc_dma_lpm_buf = res->start;
 	}
 
-	drvdata->num_lpaif = 1;
+	if (variant->mi2s_desc) {
+		if (variant->num_mi2s_desc > LPASS_MAX_LPAIF)
+			return -EINVAL;
 
-	ret = lpass_cpu_init_lpaif(pdev, 0, "lpass-lpaif");
-	if (ret)
-		return ret;
+		drvdata->num_lpaif = variant->num_mi2s_desc;
+
+		for (i = 0; i < variant->num_mi2s_desc; i++) {
+			ret = lpass_cpu_init_lpaif(pdev, i,
+						   variant->mi2s_desc[i].reg_name);
+			if (ret)
+				return ret;
+		}
+	} else {
+		drvdata->num_lpaif = 1;
+
+		ret = lpass_cpu_init_lpaif(pdev, 0, "lpass-lpaif");
+		if (ret)
+			return ret;
+	}
 
 	if (drvdata->hdmi_port_enable) {
 		drvdata->hdmiif = devm_platform_ioremap_resource_byname(pdev, "lpass-hdmiif");
@@ -1283,8 +1320,30 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 
 		port_id = mi2s_port_id(variant->dai_driver[i].id);
 
-		drvdata->mi2s_lpaif[port_id] = &drvdata->lpaif[0];
-		drvdata->mi2s_intf[port_id] = port_id;
+		if (variant->mi2s_desc) {
+			unsigned int lpaif_idx, lpaif_port;
+			const struct lpaif_mi2s_desc *desc;
+
+			for (lpaif_idx = 0; lpaif_idx < variant->num_mi2s_desc; ++lpaif_idx) {
+				desc = &variant->mi2s_desc[lpaif_idx];
+
+				for (lpaif_port = 0;
+				     lpaif_port < desc->port_desc_count;
+				     ++lpaif_port) {
+					if (desc->port_desc[lpaif_port].id !=
+							variant->dai_driver[i].id)
+						continue;
+
+					drvdata->mi2s_lpaif[port_id] =
+						&drvdata->lpaif[lpaif_idx];
+					drvdata->mi2s_intf[port_id] =
+						desc->port_desc[lpaif_port].intf;
+				}
+			}
+		} else {
+			drvdata->mi2s_lpaif[port_id] = &drvdata->lpaif[0];
+			drvdata->mi2s_intf[port_id] = port_id;
+		}
 
 		drvdata->mi2s_osr_clk[port_id] = devm_clk_get_optional(dev,
 					     variant->dai_osr_clk_names[i]);
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 2400a6d16bb8..a953db262aa0 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -1375,6 +1375,8 @@ static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
 	}
 
 	for (i = 0; i < drvdata->num_lpaif; i++) {
+		if (!drvdata->lpaif[i].drvdata)
+			continue;
 		map = drvdata->lpaif[i].map;
 		regcache_cache_only(map, true);
 		regcache_mark_dirty(map);
@@ -1398,6 +1400,8 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
 	}
 
 	for (i = 0; i < drvdata->num_lpaif; i++) {
+		if (!drvdata->lpaif[i].drvdata)
+			continue;
 		map = drvdata->lpaif[i].map;
 		regcache_cache_only(map, false);
 		ret = regcache_sync(map);
@@ -1468,6 +1472,10 @@ static int lpass_platform_init_lpaif(struct platform_device *pdev, int idx,
 	const struct lpass_variant *v = drvdata->variant;
 	int ret;
 
+	/* Only continue if already initialized by lpass-cpu */
+	if (!lpaif->drvdata)
+		return 0;
+
 	lpaif->irq = platform_get_irq_byname(pdev, irq_name);
 	if (lpaif->irq < 0)
 		return -ENODEV;
@@ -1499,12 +1507,21 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
 	const struct lpass_variant *v = drvdata->variant;
-	int ret;
+	int i, ret;
 
-	ret = lpass_platform_init_lpaif(pdev, 0, "lpass-irq-lpaif",
-			IRQF_TRIGGER_RISING);
-	if (ret)
-		return ret;
+	if (v->mi2s_desc) {
+		for (i = 0; i < v->num_mi2s_desc; i++) {
+			ret = lpass_platform_init_lpaif(pdev, i,
+							v->mi2s_desc[i].irq_name, 0);
+			if (ret)
+				return ret;
+		}
+	} else {
+		ret = lpass_platform_init_lpaif(pdev, 0, "lpass-irq-lpaif",
+						IRQF_TRIGGER_RISING);
+		if (ret)
+			return ret;
+	}
 
 	if (drvdata->codec_dma_enable) {
 		ret = regmap_write(drvdata->rxtx_lpaif_map,
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 4407996fb145..70d64bf3f073 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -20,7 +20,7 @@
 #define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
 #define LPASS_MAX_PORTS			(LPASS_MAX_PORT)
 #define LPASS_MAX_MI2S_PORTS			(8)
-#define LPASS_MAX_LPAIF				(1)
+#define LPASS_MAX_LPAIF				(7)
 #define LPASS_MAX_DMA_CHANNELS			(8)
 #define LPASS_MAX_HDMI_DMA_CHANNELS		(4)
 #define LPASS_MAX_CDC_DMA_CHANNELS		(8)
@@ -148,6 +148,19 @@ struct lpass_lpaif {
 	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
 };
 
+struct mi2s_port_desc {
+	unsigned int intf;	/* LPAIF Interface */
+	unsigned int id;	/* DAI ID */
+};
+
+/* Extended LPAIF MI2S layout */
+struct lpaif_mi2s_desc {
+	const char *reg_name;
+	const char *irq_name;
+	struct mi2s_port_desc *port_desc;
+	unsigned int port_desc_count;
+};
+
 /* Both the CPU DAI and platform drivers will access this data */
 struct lpass_data {
 
@@ -184,6 +197,7 @@ struct lpass_data {
 	/* LPAIF instance and interface index of each MI2S port */
 	struct lpass_lpaif *mi2s_lpaif[LPASS_MAX_MI2S_PORTS];
 	unsigned int mi2s_intf[LPASS_MAX_MI2S_PORTS];
+	unsigned long lpaif_port_enable;
 
 	/* low-power audio interface (LPAIF) registers */
 	void __iomem *hdmiif;
@@ -498,6 +512,13 @@ struct lpass_variant {
 	const char * const *dai_osr_clk_names;
 	const char * const *dai_bit_clk_names;
 
+	/*
+	 * Optional LPAIF MI2S instances layout when all the MI2S
+	 * ports are not available on the core LPAIF.
+	 */
+	const struct lpaif_mi2s_desc *mi2s_desc;
+	int num_mi2s_desc;
+
 	/* SOC specific clocks configuration */
 	const char **clk_name;
 	int num_clks;

-- 
2.34.1


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

* [PATCH RFC 14/23] ASoC: qcom: lpass: support LPM memory type for I2S LPAIFs
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (12 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 13/23] ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 15/23] ASoC: qcom: lpass: allocate LPM buffer memory to DAIs Neil Armstrong
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

When using MI2S on non-core LPAIFs, the hardware can only
fetch data from the LPM buffers and not from DDR memory,
like the RXTX/VA/WSA codec handlers, so add a way to describe
which LPM memory is used for each LPAIF in the MI2S
descriptor.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cpu.c |  1 +
 sound/soc/qcom/lpass.h     | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 411eecd9cbb9..9ebbc422c63c 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1338,6 +1338,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 						&drvdata->lpaif[lpaif_idx];
 					drvdata->mi2s_intf[port_id] =
 						desc->port_desc[lpaif_port].intf;
+					drvdata->mi2s_lpm[port_id] = desc->lpm_type;
 				}
 			}
 		} else {
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 70d64bf3f073..71f1274dfa89 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -153,10 +153,17 @@ struct mi2s_port_desc {
 	unsigned int id;	/* DAI ID */
 };
 
+enum lpass_lpm {
+	LPASS_LPM_NONE = 0,	/* DMA buffer in DDR */
+	LPASS_RXTX_CDC_LPM,	/* CODEC LPM */
+	LPASS_VA_CDC_LPM,	/* VA LPM */
+};
+
 /* Extended LPAIF MI2S layout */
 struct lpaif_mi2s_desc {
 	const char *reg_name;
 	const char *irq_name;
+	enum lpass_lpm lpm_type;
 	struct mi2s_port_desc *port_desc;
 	unsigned int port_desc_count;
 };
@@ -197,6 +204,7 @@ struct lpass_data {
 	/* LPAIF instance and interface index of each MI2S port */
 	struct lpass_lpaif *mi2s_lpaif[LPASS_MAX_MI2S_PORTS];
 	unsigned int mi2s_intf[LPASS_MAX_MI2S_PORTS];
+	enum lpass_lpm mi2s_lpm[LPASS_MAX_MI2S_PORTS];
 	unsigned long lpaif_port_enable;
 
 	/* low-power audio interface (LPAIF) registers */
@@ -261,6 +269,19 @@ struct lpass_data {
 	struct lpass_sstream_ctl *sstream_ctl;
 };
 
+static inline bool is_lpm_mi2s_port(struct lpass_data *drvdata, unsigned int dai_id)
+{
+	unsigned int port_id;
+
+	switch (dai_id) {
+	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
+		port_id = mi2s_port_id(dai_id);
+		return drvdata->mi2s_lpm[port_id] != LPASS_LPM_NONE;
+	}
+	return false;
+}
+
 /* Vairant data per each SOC */
 struct lpass_variant {
 	u32	irq_reg_base;

-- 
2.34.1


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

* [PATCH RFC 15/23] ASoC: qcom: lpass: allocate LPM buffer memory to DAIs
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (13 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 14/23] ASoC: qcom: lpass: support LPM memory type for I2S LPAIFs Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:12   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 16/23] ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory Neil Armstrong
                   ` (7 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

When using MI2S on non-core LPAIFs, the hardware can only
fetch data from the LPM buffers and not from DDR memory,
allocate the DMA buffers in each LPM memory for all users
and drop the static allocation.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cpu.c      |  27 ++++---
 sound/soc/qcom/lpass-platform.c | 152 ++++++++++++++++++++++++++++++++++++----
 sound/soc/qcom/lpass.h          |   9 +++
 3 files changed, 165 insertions(+), 23 deletions(-)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 9ebbc422c63c..4bbcd59ebd60 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1258,16 +1258,6 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 			dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
 			return ret;
 		}
-
-		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
-		if (!res)
-			return -EINVAL;
-		drvdata->rxtx_cdc_dma_lpm_buf = res->start;
-
-		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
-		if (!res)
-			return -EINVAL;
-		drvdata->va_cdc_dma_lpm_buf = res->start;
 	}
 
 	if (variant->mi2s_desc) {
@@ -1281,6 +1271,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 						   variant->mi2s_desc[i].reg_name);
 			if (ret)
 				return ret;
+
+			if (variant->mi2s_desc[i].lpm_type != LPASS_LPM_NONE)
+				drvdata->mi2s_lpm_enable = true;
 		}
 	} else {
 		drvdata->num_lpaif = 1;
@@ -1290,6 +1283,20 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 			return ret;
 	}
 
+	if (drvdata->codec_dma_enable || drvdata->mi2s_lpm_enable) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
+		if (!res)
+			return -EINVAL;
+		drvdata->rxtx_cdc_dma_lpm_buf = res->start;
+		drvdata->rxtx_cdc_dma_lpm_size = resource_size(res);
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
+		if (!res)
+			return -EINVAL;
+		drvdata->va_cdc_dma_lpm_buf = res->start;
+		drvdata->va_cdc_dma_lpm_size = resource_size(res);
+	}
+
 	if (drvdata->hdmi_port_enable) {
 		drvdata->hdmiif = devm_platform_ioremap_resource_byname(pdev, "lpass-hdmiif");
 		if (IS_ERR(drvdata->hdmiif))
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index a953db262aa0..75d2c872f512 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -49,6 +49,31 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
 	.fifo_size		=	0,
 };
 
+/* MI2S ports with their DMA buffer in a LPM block */
+static const struct snd_pcm_hardware lpass_platform_lpm_mi2s_hardware = {
+	.info			=	SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_PAUSE |
+					SNDRV_PCM_INFO_RESUME,
+	.formats		=	SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+	.rates			=	SNDRV_PCM_RATE_8000_192000,
+	.rate_min		=	8000,
+	.rate_max		=	192000,
+	.channels_min		=	1,
+	.channels_max		=	8,
+	.buffer_bytes_max	=	LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE,
+	.period_bytes_max	=	LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
+						LPASS_PLATFORM_PERIODS,
+	.period_bytes_min	=	LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
+						LPASS_PLATFORM_PERIODS,
+	.periods_min		=	LPASS_PLATFORM_PERIODS,
+	.periods_max		=	LPASS_PLATFORM_PERIODS,
+	.fifo_size		=	0,
+};
+
 static const struct snd_pcm_hardware lpass_platform_rxtx_hardware = {
 	.info			=	SNDRV_PCM_INFO_MMAP |
 					SNDRV_PCM_INFO_MMAP_VALID |
@@ -312,6 +337,13 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
 			dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret);
 			return ret;
 		}
+		if (is_lpm_mi2s_port(drvdata, dai_id)) {
+			snd_soc_set_runtime_hwparams(substream,
+						     &lpass_platform_lpm_mi2s_hardware);
+			runtime->dma_bytes = lpass_platform_lpm_mi2s_hardware.buffer_bytes_max;
+			snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+			break;
+		}
 		snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
 		runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
 		break;
@@ -1047,9 +1079,10 @@ static int lpass_platform_pcmops_mmap(struct snd_soc_component *component,
 {
 	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	unsigned int dai_id = cpu_dai->driver->id;
 
-	if (is_cdc_dma_port(dai_id))
+	if (is_cdc_dma_port(dai_id) || is_lpm_mi2s_port(drvdata, dai_id))
 		return lpass_platform_cdc_dma_mmap(substream, vma);
 
 	return snd_pcm_lib_default_mmap(substream, vma);
@@ -1315,23 +1348,55 @@ static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *comp
 	switch (dai_id) {
 	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
 		buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
-		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf;
+		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + drvdata->va_lpm_offset;
 		break;
 	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
 		buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
-		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
+		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
+				drvdata->va_lpm_offset + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
 		break;
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
 		buf->bytes = lpass_platform_va_hardware.buffer_bytes_max;
-		buf->addr = drvdata->va_cdc_dma_lpm_buf;
+		buf->addr = drvdata->va_cdc_dma_lpm_buf + drvdata->va_lpm_offset;
 		break;
 	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
 		buf->bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
-		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + drvdata->wsa_lpm_offset;
 		break;
 	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
 		buf->bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
-		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 3;
+		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
+				drvdata->wsa_lpm_offset + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
+		break;
+	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
+		if (is_lpm_mi2s_port(drvdata, dai_id)) {
+			unsigned int port_id = mi2s_port_id(dai_id);
+
+			buf->bytes = lpass_platform_lpm_mi2s_hardware.buffer_bytes_max;
+			switch (drvdata->mi2s_lpm[port_id]) {
+			case LPASS_RXTX_CDC_LPM:
+				if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+					buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
+						    drvdata->mi2s_lpm_offset[port_id];
+				else
+					buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
+						    drvdata->mi2s_lpm_offset[port_id] +
+						    LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
+				break;
+			case LPASS_VA_CDC_LPM:
+				if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+					buf->addr = drvdata->va_cdc_dma_lpm_buf +
+						    drvdata->mi2s_lpm_offset[port_id];
+				else
+					buf->addr = drvdata->va_cdc_dma_lpm_buf +
+						    drvdata->mi2s_lpm_offset[port_id] +
+						    LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
+				break;
+			default:
+				break;
+			}
+		}
 		break;
 	default:
 		break;
@@ -1347,15 +1412,12 @@ static int lpass_platform_pcm_new(struct snd_soc_component *component,
 {
 	struct snd_pcm *pcm = soc_runtime->pcm;
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	unsigned int dai_id = cpu_dai->driver->id;
 
 	size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
 
-	/*
-	 * Lpass codec dma can access only lpass lpm hardware memory.
-	 * ioremap is for HLOS to access hardware memory.
-	 */
-	if (is_cdc_dma_port(dai_id))
+	if (is_cdc_dma_port(dai_id) || is_lpm_mi2s_port(drvdata, dai_id))
 		return lpass_platform_prealloc_cdc_dma_buffer(component, pcm, dai_id);
 
 	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_NONCOHERENT,
@@ -1417,6 +1479,7 @@ static int lpass_platform_copy(struct snd_soc_component *component,
 			       unsigned long pos, struct iov_iter *buf,
 			       unsigned long bytes)
 {
+	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	struct snd_pcm_runtime *rt = substream->runtime;
 	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
@@ -1427,7 +1490,7 @@ static int lpass_platform_copy(struct snd_soc_component *component,
 				channel * (rt->dma_bytes / rt->channels));
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (is_cdc_dma_port(dai_id)) {
+		if (is_cdc_dma_port(dai_id) || is_lpm_mi2s_port(drvdata, dai_id)) {
 			if (copy_from_iter_toio(dma_buf, bytes, buf) != bytes)
 				ret = -EFAULT;
 		} else {
@@ -1435,7 +1498,7 @@ static int lpass_platform_copy(struct snd_soc_component *component,
 				ret = -EFAULT;
 		}
 	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		if (is_cdc_dma_port(dai_id)) {
+		if (is_cdc_dma_port(dai_id) || is_lpm_mi2s_port(drvdata, dai_id)) {
 			if (copy_to_iter_fromio(dma_buf, bytes, buf) != bytes)
 				ret = -EFAULT;
 		} else {
@@ -1464,6 +1527,64 @@ static const struct snd_soc_component_driver lpass_component_driver = {
 
 };
 
+static int lpass_platform_allocate_lpm(struct lpass_data *drvdata)
+{
+	u32 rxtx_cdc_dma_lpm_remain = drvdata->rxtx_cdc_dma_lpm_size;
+	u32 va_cdc_dma_lpm_remain = drvdata->va_cdc_dma_lpm_size;
+	u32 rxtx_cdc_dma_lpm_offset = 0;
+	u32 va_cdc_dma_lpm_offset = 0;
+	int port_id;
+
+	if (drvdata->codec_dma_enable) {
+		drvdata->rxtx_lpm_offset = rxtx_cdc_dma_lpm_offset;
+		rxtx_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+		rxtx_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+
+		drvdata->va_lpm_offset = va_cdc_dma_lpm_offset;
+		va_cdc_dma_lpm_offset += LPASS_VA_CDC_DMA_LPM_BUFF_SIZE;
+		va_cdc_dma_lpm_remain -= LPASS_VA_CDC_DMA_LPM_BUFF_SIZE;
+
+		if (drvdata->wsa_enable) {
+			drvdata->wsa_lpm_offset = rxtx_cdc_dma_lpm_offset;
+			rxtx_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+			rxtx_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+		}
+	}
+
+	if (!drvdata->mi2s_lpm_enable)
+		return 0;
+
+	for (port_id = 0; port_id < LPASS_MAX_MI2S_PORTS; port_id++) {
+		if (!test_bit(port_id, &drvdata->lpaif_port_enable))
+			continue;
+
+		switch (drvdata->mi2s_lpm[port_id]) {
+		case LPASS_RXTX_CDC_LPM:
+			if (rxtx_cdc_dma_lpm_remain < LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE)
+				return -EINVAL;
+			drvdata->mi2s_lpm_offset[port_id] = rxtx_cdc_dma_lpm_offset;
+			rxtx_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+			rxtx_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+			break;
+		case LPASS_VA_CDC_LPM:
+			/*
+			 * VA Codec uses 12KiB, but MI2S uses 8KiB
+			 * Meaning only VA _or_ a single MI2S can be enabled
+			 */
+			if (va_cdc_dma_lpm_remain < LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE)
+				return -EINVAL;
+			drvdata->mi2s_lpm_offset[port_id] = va_cdc_dma_lpm_offset;
+			va_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+			va_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
 static int lpass_platform_init_lpaif(struct platform_device *pdev, int idx,
 				     const char *irq_name, unsigned long irqflags)
 {
@@ -1629,6 +1750,11 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 			return ret;
 		}
 	}
+
+	ret = lpass_platform_allocate_lpm(drvdata);
+	if (ret)
+		return ret;
+
 	return devm_snd_soc_register_component(&pdev->dev,
 			&lpass_component_driver, NULL, 0);
 }
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 71f1274dfa89..8f361f2808ca 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -196,6 +196,7 @@ struct lpass_data {
 	int hdmi_port_enable;
 	int codec_dma_enable;
 	int wsa_enable;
+	int mi2s_lpm_enable;
 
 	/* LPAIF instances, 0 is the core "lpass-lpaif" one */
 	struct lpass_lpaif lpaif[LPASS_MAX_LPAIF];
@@ -214,7 +215,15 @@ struct lpass_data {
 	void __iomem *wsa_lpaif;
 
 	u32 rxtx_cdc_dma_lpm_buf;
+	u32 rxtx_cdc_dma_lpm_size;
 	u32 va_cdc_dma_lpm_buf;
+	u32 va_cdc_dma_lpm_size;
+
+	/* LPM buffer assignment */
+	u32 rxtx_lpm_offset;
+	u32 va_lpm_offset;
+	u32 wsa_lpm_offset;
+	u32 mi2s_lpm_offset[LPASS_MAX_MI2S_PORTS];
 
 	/* regmap backed by the low-power audio interface (LPAIF) registers */
 	struct regmap *hdmiif_map;

-- 
2.34.1


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

* [PATCH RFC 16/23] ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (14 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 15/23] ASoC: qcom: lpass: allocate LPM buffer memory to DAIs Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:06   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 17/23] ASoC: qcom: add SM8650 LPASS CPU driver Neil Armstrong
                   ` (6 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

The LPM memory zone needs different codec_mem clock to be
enabled depending on the DMA buffer offset, add an helper
returning the right clock and add support for the MI2S
ports using a non-core LPAIF.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/lpass-cdc-dma.c | 27 ++++++++++++---------------
 sound/soc/qcom/lpass-cpu.c     | 21 +++++++++++++++++++++
 sound/soc/qcom/lpass.h         | 20 ++++++++++++++++++++
 3 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
index 903055e3188d..c20e0976563b 100644
--- a/sound/soc/qcom/lpass-cdc-dma.c
+++ b/sound/soc/qcom/lpass-cdc-dma.c
@@ -199,12 +199,14 @@ static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+	struct clk *mem_clk;
 
 	switch (dai->id) {
 	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
 	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
-		clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);
-		clk_prepare_enable(drvdata->codec_mem0);
+		mem_clk = lpass_cdc_dma_mem_clk(drvdata, drvdata->rxtx_lpm_offset);
+		clk_set_rate(mem_clk, CODEC_MEM_HZ_NORMAL);
+		clk_prepare_enable(mem_clk);
 		break;
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
 		clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL);
@@ -212,14 +214,9 @@ static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
 		break;
 	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
 	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
-		clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);	//??
-		clk_set_rate(drvdata->codec_mem1, CODEC_MEM_HZ_NORMAL);	//??
-		clk_set_rate(drvdata->codec_mem2, CODEC_MEM_HZ_NORMAL);	//??
-		clk_set_rate(drvdata->codec_mem3, CODEC_MEM_HZ_NORMAL);	//??
-		clk_prepare_enable(drvdata->codec_mem0);		//??
-		clk_prepare_enable(drvdata->codec_mem1);		//??
-		clk_prepare_enable(drvdata->codec_mem2);		//??
-		clk_prepare_enable(drvdata->codec_mem3);		//??
+		mem_clk = lpass_cdc_dma_mem_clk(drvdata, drvdata->wsa_lpm_offset);
+		clk_set_rate(mem_clk, CODEC_MEM_HZ_NORMAL);
+		clk_prepare_enable(mem_clk);
 		break;
 	default:
 		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
@@ -233,21 +230,21 @@ static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream,
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+	struct clk *mem_clk;
 
 	switch (dai->id) {
 	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
 	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
-		clk_disable_unprepare(drvdata->codec_mem0);
+		mem_clk = lpass_cdc_dma_mem_clk(drvdata, drvdata->rxtx_lpm_offset);
+		clk_disable_unprepare(mem_clk);
 		break;
 	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
 		clk_disable_unprepare(drvdata->va_mem0);
 		break;
 	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
 	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
-		clk_disable_unprepare(drvdata->codec_mem3);
-		clk_disable_unprepare(drvdata->codec_mem2);
-		clk_disable_unprepare(drvdata->codec_mem1);
-		clk_disable_unprepare(drvdata->codec_mem0);
+		mem_clk = lpass_cdc_dma_mem_clk(drvdata, drvdata->wsa_lpm_offset);
+		clk_disable_unprepare(mem_clk);
 		break;
 	default:
 		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 4bbcd59ebd60..302c31b23090 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -89,6 +89,17 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
 	unsigned int port_id = mi2s_port_id(dai->driver->id);
 	int ret;
 
+	if (is_lpm_mi2s_port(drvdata, dai->driver->id)) {
+		struct clk *mem_clk = drvdata->va_mem0;
+
+		if (drvdata->mi2s_lpm[port_id] == LPASS_RXTX_CDC_LPM)
+			mem_clk = lpass_cdc_dma_mem_clk(drvdata,
+							drvdata->mi2s_lpm_offset[port_id]);
+
+		clk_set_rate(mem_clk, LPASS_CODEC_MEM_HZ_NORMAL);
+		clk_prepare_enable(mem_clk);
+	}
+
 	ret = clk_prepare_enable(drvdata->mi2s_osr_clk[port_id]);
 	if (ret) {
 		dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);
@@ -133,6 +144,16 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
 	}
 
 	clk_unprepare(drvdata->mi2s_bit_clk[port_id]);
+
+	if (is_lpm_mi2s_port(drvdata, dai->driver->id)) {
+		struct clk *mem_clk = drvdata->va_mem0;
+
+		if (drvdata->mi2s_lpm[port_id] == LPASS_RXTX_CDC_LPM)
+			mem_clk = lpass_cdc_dma_mem_clk(drvdata,
+							drvdata->mi2s_lpm_offset[port_id]);
+
+		clk_disable_unprepare(mem_clk);
+	}
 }
 
 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 8f361f2808ca..699b9a07eb5a 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -30,6 +30,8 @@
 #define LPASS_CDC_DMA_INTF_FOUR_CHANNEL		(0x0F)
 #define LPASS_CDC_DMA_INTF_SIX_CHANNEL		(0x3F)
 #define LPASS_CDC_DMA_INTF_EIGHT_CHANNEL	(0xFF)
+#define LPASS_CDC_DMA_LPM_MEM_BLK_SIZE (2 * 8 * 1024)
+#define LPASS_CODEC_MEM_HZ_NORMAL		153600000
 
 #define LPASS_ACTIVE_PDS			(4)
 #define LPASS_PROXY_PDS			(8)
@@ -291,6 +293,24 @@ static inline bool is_lpm_mi2s_port(struct lpass_data *drvdata, unsigned int dai
 	return false;
 }
 
+static inline struct clk *lpass_cdc_dma_mem_clk(struct lpass_data *drvdata, u32 offset)
+{
+	unsigned int id = offset / LPASS_CDC_DMA_LPM_MEM_BLK_SIZE;
+
+	switch (id) {
+	case 0:
+		return drvdata->codec_mem0;
+	case 1:
+		return drvdata->codec_mem1;
+	case 2:
+		return drvdata->codec_mem2;
+	case 3:
+		return drvdata->codec_mem3;
+	default:
+		return NULL;
+	}
+}
+
 /* Vairant data per each SOC */
 struct lpass_variant {
 	u32	irq_reg_base;

-- 
2.34.1


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

* [PATCH RFC 17/23] ASoC: qcom: add SM8650 LPASS CPU driver
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (15 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 16/23] ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:22   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 18/23] ASoC: qcom: swd: allow LPASS_CDC_DMA_WSA_RX0 as soundwire dai Neil Armstrong
                   ` (5 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Without the ADSP firmware, the SM8650 LPASS audio interfaces must be
driven directly by the application processor.

Add the SM8650 lpass-cpu variant with the Primary, Secondary, Tertiary,
Senary & Septenary MI2S, DP and codec DMA DAIs, their LPAIF instances,
clocks and LPM DMA buffers.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/Kconfig        |  12 +
 sound/soc/qcom/Makefile       |   2 +
 sound/soc/qcom/lpass-sm8650.c | 651 ++++++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/lpass.h        |   2 +-
 4 files changed, 666 insertions(+), 1 deletion(-)

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index f6d810931a9a..d9b626cd1bc9 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -47,6 +47,18 @@ config SND_SOC_LPASS_SC7280
 	select SND_SOC_LPASS_HDMI
 	select SND_SOC_LPASS_CDC_DMA
 
+config SND_SOC_LPASS_SM8650
+	tristate "Qualcomm SM8650 LPASS CPU DAI support"
+	depends on COMMON_CLK
+	select SND_SOC_LPASS_CPU
+	select SND_SOC_LPASS_PLATFORM
+	select SND_SOC_LPASS_HDMI
+	select SND_SOC_LPASS_CDC_DMA
+	help
+	  Support for the MI2S, codec DMA & DP interfaces of the Qualcomm
+	  SM8650 LPASS, when driven by Linux instead of the ADSP firmware.
+	  Say Y or M if you want to use audio on such SM8650 based boards.
+
 config SND_SOC_STORM
 	tristate "ASoC I2S support for Storm boards"
 	depends on GPIOLIB
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index cf50b7a24bcf..b2c5f9f387b6 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -8,6 +8,7 @@ snd-soc-lpass-ipq806x-y := lpass-ipq806x.o
 snd-soc-lpass-apq8016-y := lpass-apq8016.o
 snd-soc-lpass-sc7180-y := lpass-sc7180.o
 snd-soc-lpass-sc7280-y := lpass-sc7280.o
+snd-soc-lpass-sm8650-y := lpass-sm8650.o
 
 obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
 obj-$(CONFIG_SND_SOC_LPASS_CDC_DMA) += snd-soc-lpass-cdc-dma.o
@@ -17,6 +18,7 @@ obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
 obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
 obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o
 obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o
+obj-$(CONFIG_SND_SOC_LPASS_SM8650) += snd-soc-lpass-sm8650.o
 
 # Machine
 snd-soc-storm-y := storm.o
diff --git a/sound/soc/qcom/lpass-sm8650.c b/sound/soc/qcom/lpass-sm8650.c
new file mode 100644
index 000000000000..42050f56a484
--- /dev/null
+++ b/sound/soc/qcom/lpass-sm8650.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based on lpass-sc7280.c:
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2026, Linaro Limited. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/pm.h>
+
+#include <dt-bindings/sound/qcom,lpass.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver sm8650_lpass_cpu_dai_driver[] = {
+	{
+		.id = MI2S_PRIMARY,
+		.name = "Primary MI2S",
+		.playback = {
+			.stream_name = "Primary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = MI2S_SECONDARY,
+		.name = "Secondary MI2S",
+		.playback = {
+			.stream_name = "Secondary MI2S Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = MI2S_TERTIARY,
+		.name = "Tertiary MI2S",
+		.playback = {
+			.stream_name = "Tertiary MI2S Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = MI2S_QUATERNARY,
+		.name = "Quaternary MI2S",
+		.playback = {
+			.stream_name = "Quaternary MI2S Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = MI2S_QUINARY,
+		.name = "Quinary MI2S",
+		.playback = {
+			.stream_name = "Quinary MI2S Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = MI2S_SENARY,
+		.name = "Senary MI2S",
+		.playback = {
+			.stream_name = "Senary MI2S Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = MI2S_SEPTENARY,
+		.name = "Septenary MI2S",
+		.playback = {
+			.stream_name = "Septenary MI2S Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cpu_dai_ops,
+	}, {
+		.id = LPASS_DP_RX,
+		.name = "Hdmi",
+		.playback = {
+			.stream_name = "DP Playback",
+			.formats = SNDRV_PCM_FMTBIT_S24,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_hdmi_dai_ops,
+	}, {
+		.id = LPASS_CDC_DMA_RX0,
+		.name = "CDC DMA RX",
+		.playback = {
+			.stream_name = "WCD Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cdc_dma_dai_ops,
+	}, {
+		.id = LPASS_CDC_DMA_TX3,
+		.name = "CDC DMA TX",
+		.capture = {
+			.stream_name = "WCD Capture",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 1,
+			.channels_max	= 1,
+		},
+		.ops	= &asoc_qcom_lpass_cdc_dma_dai_ops,
+	}, {
+		.id = LPASS_CDC_DMA_VA_TX0,
+		.name = "CDC DMA VA",
+		.capture = {
+			.stream_name = "DMIC Capture",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 4,
+		},
+		.ops	= &asoc_qcom_lpass_cdc_dma_dai_ops,
+	}, {
+		.id = LPASS_CDC_DMA_WSA_RX0,
+		.name = "CDC DMA WSA RX",
+		.playback = {
+			.stream_name = "WSA Playback",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_lpass_cdc_dma_dai_ops,
+	},
+};
+
+static int sm8650_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+					  int direction, unsigned int dai_id)
+{
+	const struct lpass_variant *v = drvdata->variant;
+	unsigned long *ch_map;
+	unsigned int port_id;
+	int chan = 0;
+
+	switch (dai_id) {
+	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
+		port_id = mi2s_port_id(dai_id);
+		ch_map = &drvdata->mi2s_lpaif[port_id]->dma_ch_bit_map;
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			chan = find_first_zero_bit(ch_map, v->rdma_channels);
+
+			if (chan >= v->rdma_channels)
+				return -EBUSY;
+		} else {
+			chan = find_next_zero_bit(ch_map,
+						  v->wrdma_channel_start +
+						  v->wrdma_channels,
+						  v->wrdma_channel_start);
+
+			if (chan >= v->wrdma_channel_start + v->wrdma_channels)
+				return -EBUSY;
+		}
+		set_bit(chan, ch_map);
+		break;
+	case LPASS_DP_RX:
+		chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map,
+					   v->hdmi_rdma_channels);
+		if (chan >= v->hdmi_rdma_channels)
+			return -EBUSY;
+		set_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
+		break;
+	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+		chan = find_first_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
+					   v->rxtx_rdma_channels);
+		if (chan >= v->rxtx_rdma_channels)
+			return -EBUSY;
+		break;
+	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+		chan = find_next_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
+					  v->rxtx_wrdma_channel_start +
+					  v->rxtx_wrdma_channels,
+					  v->rxtx_wrdma_channel_start);
+		if (chan >= v->rxtx_wrdma_channel_start + v->rxtx_wrdma_channels)
+			return -EBUSY;
+		set_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
+		break;
+	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+		chan = find_next_zero_bit(&drvdata->va_dma_ch_bit_map,
+					  v->va_wrdma_channel_start +
+					  v->va_wrdma_channels,
+					  v->va_wrdma_channel_start);
+		if (chan >= v->va_wrdma_channel_start + v->va_wrdma_channels)
+			return -EBUSY;
+		set_bit(chan, &drvdata->va_dma_ch_bit_map);
+		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+		chan = find_first_zero_bit(&drvdata->wsa_dma_ch_bit_map,
+					   v->wsa_rdma_channels);
+		if (chan >= v->wsa_rdma_channels)
+			return -EBUSY;
+		break;
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		chan = find_next_zero_bit(&drvdata->wsa_dma_ch_bit_map,
+					  v->wsa_wrdma_channel_start +
+					  v->wsa_wrdma_channels,
+					  v->wsa_wrdma_channel_start);
+		if (chan >= v->wsa_wrdma_channel_start + v->wsa_wrdma_channels)
+			return -EBUSY;
+		set_bit(chan, &drvdata->wsa_dma_ch_bit_map);
+		break;
+	default:
+		break;
+	}
+
+	return chan;
+}
+
+static int sm8650_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
+{
+	unsigned int port_id;
+
+	switch (dai_id) {
+	case MI2S_PRIMARY ... MI2S_QUINARY:
+	case MI2S_SENARY ... MI2S_SEPTENARY:
+		port_id = mi2s_port_id(dai_id);
+		clear_bit(chan, &drvdata->mi2s_lpaif[port_id]->dma_ch_bit_map);
+		break;
+	case LPASS_DP_RX:
+		clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
+		break;
+	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
+	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
+		clear_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
+		break;
+	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
+		clear_bit(chan, &drvdata->va_dma_ch_bit_map);
+		break;
+	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
+	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
+		clear_bit(chan, &drvdata->wsa_dma_ch_bit_map);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int sm8650_lpass_init(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	const struct lpass_variant *variant = drvdata->variant;
+	struct device *dev = &pdev->dev;
+	int ret, i;
+
+	drvdata->clks = devm_kcalloc(dev, variant->num_clks,
+				     sizeof(*drvdata->clks), GFP_KERNEL);
+	if (!drvdata->clks)
+		return -ENOMEM;
+
+	drvdata->num_clks = variant->num_clks;
+
+	for (i = 0; i < drvdata->num_clks; i++)
+		drvdata->clks[i].id = variant->clk_name[i];
+
+	ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);
+	if (ret) {
+		dev_err(dev, "Failed to get clocks %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+	if (ret) {
+		dev_err(dev, "sm8650 clk_enable failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sm8650_lpass_exit(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+	return 0;
+}
+
+static int sm8650_lpass_dev_resume(struct device *dev)
+{
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+
+	return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+}
+
+static int sm8650_lpass_dev_suspend(struct device *dev)
+{
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+
+	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+	return 0;
+}
+
+static const struct dev_pm_ops sm8650_lpass_pm_ops = {
+	SYSTEM_SLEEP_PM_OPS(sm8650_lpass_dev_suspend, sm8650_lpass_dev_resume)
+};
+
+/* MI2S mapping over LPAIF instances */
+static const struct lpaif_mi2s_desc sm8650_lpaif_mi2s_desc[] = {
+	{
+		.reg_name = "lpass-lpaif",
+		.irq_name = "lpass-irq-lpaif",
+		.lpm_type = LPASS_LPM_NONE,
+		.port_desc = (const struct mi2s_port_desc[]) {
+			{ .intf = 0, .id = MI2S_PRIMARY, },
+			{ .intf = 1, .id = MI2S_SECONDARY, },
+		},
+		.port_desc_count = 2,
+	},
+	{
+		.reg_name = "lpass-rxtx-lpaif",
+		.irq_name = "lpass-irq-rxtxif",
+		.lpm_type = LPASS_VA_CDC_LPM,
+		.port_desc = (const struct mi2s_port_desc[]) {
+			{ .intf = 0, .id = MI2S_QUATERNARY, },
+		},
+		.port_desc_count = 1,
+	},
+	{
+		.reg_name = "lpass-va-lpaif",
+		.irq_name = "lpass-irq-vaif",
+		.lpm_type = LPASS_VA_CDC_LPM,
+		.port_desc = (const struct mi2s_port_desc[]) {
+			{ .intf = 0, .id = MI2S_QUINARY, },
+		},
+		.port_desc_count = 1,
+	},
+	{
+		.reg_name = "lpass-wsa-lpaif",
+		.irq_name = "lpass-irq-wsaif",
+		.lpm_type = LPASS_RXTX_CDC_LPM,
+		.port_desc = (const struct mi2s_port_desc[]) {
+			{ .intf = 0, .id = MI2S_SENARY, },
+		},
+		.port_desc_count = 1,
+	},
+	{
+		.reg_name = "lpass-aud-lpaif",
+		.irq_name = "lpass-irq-audif",
+		.lpm_type = LPASS_RXTX_CDC_LPM,
+		.port_desc = (const struct mi2s_port_desc[]) {
+			{ .intf = 0, .id = MI2S_SEPTENARY, },
+			{ .intf = 1, .id = MI2S_TERTIARY, },
+		},
+		.port_desc_count = 2,
+	},
+};
+
+static const struct lpass_variant sm8650_data = {
+	.i2sctrl_reg_base		= 0x1000,
+	.i2sctrl_reg_stride		= 0x1000,
+	.i2s_ports			= 3,
+	.irq_reg_base			= 0x9000,
+	.irq_reg_stride			= 0x1000,
+	.irq_ports			= 5,
+	.rdma_reg_base			= 0xe000,
+	.rdma_reg_stride		= 0x1000,
+	.rdma_channels			= 3,
+	.rxtx_rdma_reg_base		= 0xe000,
+	.rxtx_rdma_reg_stride		= 0x1000,
+	.rxtx_rdma_channels		= 10,
+	.wsa_rdma_reg_base		= 0xe000,
+	.wsa_rdma_reg_stride		= 0x1000,
+	.wsa_rdma_channels		= 10,
+	.hdmi_rdma_reg_base		= 0x64000,
+	.hdmi_rdma_reg_stride		= 0x1000,
+	.hdmi_rdma_channels		= 1,
+	.dmactl_audif_start		= 1,
+	.wrdma_reg_base			= 0x1a000,
+	.wrdma_reg_stride		= 0x1000,
+	.wrdma_channel_start		= 5,
+	.wrdma_channels			= 3,
+	.rxtx_irq_reg_base		= 0x9000,
+	.rxtx_irq_reg_stride		= 0x1000,
+	.rxtx_irq_ports			= 5,
+	.rxtx_wrdma_reg_base		= 0x1a000,
+	.rxtx_wrdma_reg_stride		= 0x1000,
+	.rxtx_wrdma_channel_start	= 5,
+	.rxtx_wrdma_channels		= 7,
+	.va_wrdma_reg_base		= 0x1a000,
+	.va_wrdma_reg_stride		= 0x1000,
+	.va_wrdma_channel_start		= 5,
+	.va_wrdma_channels		= 3,
+	.va_irq_reg_base		= 0x9000,
+	.va_irq_reg_stride		= 0x1000,
+	.va_irq_ports			= 5,
+	.wsa_irq_reg_base		= 0x9000,
+	.wsa_irq_reg_stride		= 0x1000,
+	.wsa_irq_ports			= 5,
+	.wsa_wrdma_reg_base		= 0x1a000,
+	.wsa_wrdma_reg_stride		= 0x1000,
+	.wsa_wrdma_channel_start	= 5,
+	.wsa_wrdma_channels		= 7,
+
+	.loopback			= REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
+	.spken				= REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
+	.spkmode			= REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
+	.spkmono			= REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
+	.micen				= REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
+	.micmode			= REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
+	.micmono			= REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
+	.wssrc				= REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
+	.bitwidth			= REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000),
+
+	.rdma_dyncclk			= REG_FIELD_ID(0xe000, 21, 21, 3, 0x1000),
+	.rdma_bursten			= REG_FIELD_ID(0xe000, 20, 20, 3, 0x1000),
+	.rdma_wpscnt			= REG_FIELD_ID(0xe000, 16, 19, 3, 0x1000),
+	.rdma_intf			= REG_FIELD_ID(0xe000, 12, 15, 3, 0x1000),
+	.rdma_fifowm			= REG_FIELD_ID(0xe000, 1, 5, 3, 0x1000),
+	.rdma_enable			= REG_FIELD_ID(0xe000, 0, 0, 3, 0x1000),
+
+	.wrdma_dyncclk			= REG_FIELD_ID(0x1a000, 22, 22, 3, 0x1000),
+	.wrdma_bursten			= REG_FIELD_ID(0x1a000, 21, 21, 3, 0x1000),
+	.wrdma_wpscnt			= REG_FIELD_ID(0x1a000, 17, 20, 3, 0x1000),
+	.wrdma_intf			= REG_FIELD_ID(0x1a000, 12, 16, 3, 0x1000),
+	.wrdma_fifowm			= REG_FIELD_ID(0x1a000, 1, 5, 3, 0x1000),
+	.wrdma_enable			= REG_FIELD_ID(0x1a000, 0, 0, 3, 0x1000),
+
+	.rxtx_rdma_dyncclk		= REG_FIELD_ID(0xe000, 21, 21, 10, 0x1000),
+	.rxtx_rdma_bursten		= REG_FIELD_ID(0xe000, 20, 20, 10, 0x1000),
+	.rxtx_rdma_wpscnt		= REG_FIELD_ID(0xe000, 16, 19, 10, 0x1000),
+	.rxtx_rdma_intf			= REG_FIELD_ID(0xe000, 12, 15, 10, 0x1000),
+	.rxtx_rdma_fifowm		= REG_FIELD_ID(0xe000, 1, 11, 10, 0x1000),
+	.rxtx_rdma_enable		= REG_FIELD_ID(0xe000, 0, 0, 10, 0x1000),
+
+	.rxtx_rdma_codec_enable		= REG_FIELD_ID(0xe050, 30, 30, 10, 0x1000),
+	.rxtx_rdma_codec_pack		= REG_FIELD_ID(0xe050, 29, 29, 10, 0x1000),
+	.rxtx_rdma_codec_fs_sel		= REG_FIELD_ID(0xe050, 25, 27, 10, 0x1000),
+	.rxtx_rdma_codec_fs_delay	= REG_FIELD_ID(0xe050, 21, 24, 10, 0x1000),
+	.rxtx_rdma_codec_intf		= REG_FIELD_ID(0xe050, 16, 19, 10, 0x1000),
+	.rxtx_rdma_codec_ch		= REG_FIELD_ID(0xe050, 0, 7, 10, 0x1000),
+
+	.rxtx_wrdma_dyncclk		= REG_FIELD_ID(0x1a000, 22, 22, 7, 0x1000),
+	.rxtx_wrdma_bursten		= REG_FIELD_ID(0x1a000, 21, 21, 7, 0x1000),
+	.rxtx_wrdma_wpscnt		= REG_FIELD_ID(0x1a000, 17, 20, 7, 0x1000),
+	.rxtx_wrdma_intf		= REG_FIELD_ID(0x1a000, 12, 16, 7, 0x1000),
+	.rxtx_wrdma_fifowm		= REG_FIELD_ID(0x1a000, 1, 11, 7, 0x1000),
+	.rxtx_wrdma_enable		= REG_FIELD_ID(0x1a000, 0, 0, 7, 0x1000),
+
+	.rxtx_wrdma_codec_enable	= REG_FIELD_ID(0x1a050, 30, 30, 7, 0x1000),
+	.rxtx_wrdma_codec_pack		= REG_FIELD_ID(0x1a050, 29, 29, 7, 0x1000),
+	.rxtx_wrdma_codec_fs_sel	= REG_FIELD_ID(0x1a050, 25, 27, 7, 0x1000),
+	.rxtx_wrdma_codec_fs_delay	= REG_FIELD_ID(0x1a050, 21, 24, 7, 0x1000),
+	.rxtx_wrdma_codec_intf		= REG_FIELD_ID(0x1a050, 16, 19, 7, 0x1000),
+	.rxtx_wrdma_codec_ch		= REG_FIELD_ID(0x1a050, 0, 7, 7, 0x1000),
+
+	.va_wrdma_dyncclk		= REG_FIELD_ID(0x1a000, 22, 22, 3, 0x1000),
+	.va_wrdma_bursten		= REG_FIELD_ID(0x1a000, 21, 21, 3, 0x1000),
+	.va_wrdma_wpscnt		= REG_FIELD_ID(0x1a000, 17, 20, 3, 0x1000),
+	.va_wrdma_intf			= REG_FIELD_ID(0x1a000, 12, 16, 3, 0x1000),
+	.va_wrdma_fifowm		= REG_FIELD_ID(0x1a000, 1, 11, 3, 0x1000),
+	.va_wrdma_enable		= REG_FIELD_ID(0x1a000, 0, 0, 3, 0x1000),
+
+	.va_wrdma_codec_enable		= REG_FIELD_ID(0x1a050, 30, 30, 3, 0x1000),
+	.va_wrdma_codec_pack		= REG_FIELD_ID(0x1a050, 29, 29, 3, 0x1000),
+	.va_wrdma_codec_fs_sel		= REG_FIELD_ID(0x1a050, 25, 27, 3, 0x1000),
+	.va_wrdma_codec_fs_delay	= REG_FIELD_ID(0x1a050, 21, 24, 3, 0x1000),
+	.va_wrdma_codec_intf		= REG_FIELD_ID(0x1a050, 16, 19, 3, 0x1000),
+	.va_wrdma_codec_ch		= REG_FIELD_ID(0x1a050, 0, 7, 3, 0x1000),
+
+	.wsa_rdma_dyncclk		= REG_FIELD_ID(0xe000, 21, 21, 10, 0x1000),
+	.wsa_rdma_bursten		= REG_FIELD_ID(0xe000, 20, 20, 10, 0x1000),
+	.wsa_rdma_wpscnt		= REG_FIELD_ID(0xe000, 16, 19, 10, 0x1000),
+	.wsa_rdma_intf			= REG_FIELD_ID(0xe000, 12, 15, 10, 0x1000),
+	.wsa_rdma_fifowm		= REG_FIELD_ID(0xe000, 1, 11, 10, 0x1000),
+	.wsa_rdma_enable		= REG_FIELD_ID(0xe000, 0, 0, 10, 0x1000),
+
+	.wsa_rdma_codec_enable		= REG_FIELD_ID(0xe050, 30, 30, 10, 0x1000),
+	.wsa_rdma_codec_pack		= REG_FIELD_ID(0xe050, 29, 29, 10, 0x1000),
+	.wsa_rdma_codec_fs_sel		= REG_FIELD_ID(0xe050, 25, 27, 10, 0x1000),
+	.wsa_rdma_codec_fs_delay	= REG_FIELD_ID(0xe050, 21, 24, 10, 0x1000),
+	.wsa_rdma_codec_intf		= REG_FIELD_ID(0xe050, 16, 19, 10, 0x1000),
+	.wsa_rdma_codec_ch		= REG_FIELD_ID(0xe050, 0, 7, 10, 0x1000),
+
+	.wsa_wrdma_dyncclk		= REG_FIELD_ID(0x1a000, 22, 22, 7, 0x1000),
+	.wsa_wrdma_bursten		= REG_FIELD_ID(0x1a000, 21, 21, 7, 0x1000),
+	.wsa_wrdma_wpscnt		= REG_FIELD_ID(0x1a000, 17, 20, 7, 0x1000),
+	.wsa_wrdma_intf			= REG_FIELD_ID(0x1a000, 12, 16, 7, 0x1000),
+	.wsa_wrdma_fifowm		= REG_FIELD_ID(0x1a000, 1, 11, 7, 0x1000),
+	.wsa_wrdma_enable		= REG_FIELD_ID(0x1a000, 0, 0, 7, 0x1000),
+
+	.wsa_wrdma_codec_enable		= REG_FIELD_ID(0x1a050, 30, 30, 7, 0x1000),
+	.wsa_wrdma_codec_pack		= REG_FIELD_ID(0x1a050, 29, 29, 7, 0x1000),
+	.wsa_wrdma_codec_fs_sel		= REG_FIELD_ID(0x1a050, 25, 27, 7, 0x1000),
+	.wsa_wrdma_codec_fs_delay	= REG_FIELD_ID(0x1a050, 21, 24, 7, 0x1000),
+	.wsa_wrdma_codec_intf		= REG_FIELD_ID(0x1a050, 16, 19, 7, 0x1000),
+	.wsa_wrdma_codec_ch		= REG_FIELD_ID(0x1a050, 0, 7, 7, 0x1000),
+
+	.hdmi_tx_ctl_addr		= 0x1000,
+	.hdmi_legacy_addr		= 0x1008,
+	.hdmi_vbit_addr			= 0x610c0,
+	.hdmi_ch_lsb_addr		= 0x61048,
+	.hdmi_ch_msb_addr		= 0x6104c,
+	.ch_stride			= 0x8,
+	.hdmi_parity_addr		= 0x61034,
+	.hdmi_dmactl_addr		= 0x61038,
+	.hdmi_dma_stride		= 0x4,
+	.hdmi_DP_addr			= 0x610c8,
+	.hdmi_sstream_addr		= 0x6101c,
+	.hdmi_irq_reg_base		= 0x63000,
+	.hdmi_irq_ports			= 1,
+
+	.hdmi_rdma_dyncclk		= REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
+	.hdmi_rdma_bursten		= REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
+	.hdmi_rdma_burst8		= REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
+	.hdmi_rdma_burst16		= REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
+	.hdmi_rdma_dynburst		= REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
+	.hdmi_rdma_wpscnt		= REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
+	.hdmi_rdma_fifowm		= REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
+	.hdmi_rdma_enable		= REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
+
+	.sstream_en			= REG_FIELD(0x6101c, 0, 0),
+	.dma_sel			= REG_FIELD(0x6101c, 1, 2),
+	.auto_bbit_en			= REG_FIELD(0x6101c, 3, 3),
+	.layout				= REG_FIELD(0x6101c, 4, 4),
+	.layout_sp			= REG_FIELD(0x6101c, 5, 8),
+	.set_sp_on_en			= REG_FIELD(0x6101c, 10, 10),
+	.dp_audio			= REG_FIELD(0x6101c, 11, 11),
+	.dp_staffing_en			= REG_FIELD(0x6101c, 12, 12),
+	.dp_sp_b_hw_en			= REG_FIELD(0x6101c, 13, 13),
+
+	.mute				= REG_FIELD(0x610c8, 0, 0),
+	.as_sdp_cc			= REG_FIELD(0x610c8, 1, 3),
+	.as_sdp_ct			= REG_FIELD(0x610c8, 4, 7),
+	.aif_db4			= REG_FIELD(0x610c8, 8, 15),
+	.frequency			= REG_FIELD(0x610c8, 16, 21),
+	.mst_index			= REG_FIELD(0x610c8, 28, 29),
+	.dptx_index			= REG_FIELD(0x610c8, 30, 31),
+
+	.soft_reset			= REG_FIELD(0x1000, 31, 31),
+	.force_reset			= REG_FIELD(0x1000, 30, 30),
+
+	.use_hw_chs			= REG_FIELD(0x61038, 0, 0),
+	.use_hw_usr			= REG_FIELD(0x61038, 1, 1),
+	.hw_chs_sel			= REG_FIELD(0x61038, 2, 4),
+	.hw_usr_sel			= REG_FIELD(0x61038, 5, 6),
+
+	.replace_vbit			= REG_FIELD(0x610c0, 0, 0),
+	.vbit_stream			= REG_FIELD(0x610c0, 1, 1),
+
+	.legacy_en			=  REG_FIELD(0x1008, 0, 0),
+	.calc_en			=  REG_FIELD(0x61034, 0, 0),
+	.lsb_bits			=  REG_FIELD(0x61048, 0, 31),
+	.msb_bits			=  REG_FIELD(0x6104c, 0, 31),
+
+	.clk_name			= (const char*[]) {
+							"core_cc_sysnoc_mport_core",
+						},
+	.num_clks			= 1,
+
+	.dai_driver			= sm8650_lpass_cpu_dai_driver,
+	.num_dai			= ARRAY_SIZE(sm8650_lpass_cpu_dai_driver),
+	.dai_osr_clk_names		= (const char *[]) {
+							"core_cc_ext_mclk0",
+							"core_cc_ext_mclk0",
+							"core_cc_ext_mclk0",
+							"core_cc_ext_mclk0",
+							"core_cc_ext_mclk0",
+							"core_cc_ext_mclk0",
+							"core_cc_ext_mclk0",
+							},
+	.dai_bit_clk_names		= (const char *[]) {
+							"core_cc_ext_if0_ibit",
+							"core_cc_ext_if1_ibit",
+							"audio_cc_ext_if5_ibit",
+							"audio_cc_ext_if1_ibit",
+							"aon_cc_ext_if1_ibit",
+							"audio_cc_ext_if2_ibit",
+							"audio_cc_ext_if4_ibit",
+							},
+	.mi2s_desc			= sm8650_lpaif_mi2s_desc,
+	.num_mi2s_desc			= ARRAY_SIZE(sm8650_lpaif_mi2s_desc),
+	.init				= sm8650_lpass_init,
+	.exit				= sm8650_lpass_exit,
+	.alloc_dma_channel		= sm8650_lpass_alloc_dma_channel,
+	.free_dma_channel		= sm8650_lpass_free_dma_channel,
+};
+
+static const struct of_device_id sm8650_lpass_cpu_device_id[] = {
+	{.compatible = "qcom,sm8650-lpass-cpu", .data = &sm8650_data},
+	{}
+};
+MODULE_DEVICE_TABLE(of, sm8650_lpass_cpu_device_id);
+
+static struct platform_driver sm8650_lpass_cpu_platform_driver = {
+	.driver = {
+		.name = "sm8650-lpass-cpu",
+		.of_match_table = of_match_ptr(sm8650_lpass_cpu_device_id),
+		.pm = pm_ptr(&sm8650_lpass_pm_ops),
+	},
+	.probe = asoc_qcom_lpass_cpu_platform_probe,
+	.remove = asoc_qcom_lpass_cpu_platform_remove,
+	.shutdown = asoc_qcom_lpass_cpu_platform_shutdown,
+};
+
+module_platform_driver(sm8650_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("SM8650 LPASS CPU DRIVER");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 699b9a07eb5a..5304ff073b63 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -166,7 +166,7 @@ struct lpaif_mi2s_desc {
 	const char *reg_name;
 	const char *irq_name;
 	enum lpass_lpm lpm_type;
-	struct mi2s_port_desc *port_desc;
+	const struct mi2s_port_desc *port_desc;
 	unsigned int port_desc_count;
 };
 

-- 
2.34.1


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

* [PATCH RFC 18/23] ASoC: qcom: swd: allow LPASS_CDC_DMA_WSA_RX0 as soundwire dai
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (16 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 17/23] ASoC: qcom: add SM8650 LPASS CPU driver Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 19/23] ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu Neil Armstrong
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Like for the WCD ports, allow the WSA_RX0 port to be initialized
as a soundwire DAI.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/sdw.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c
index 6576b47a4c8c..82a12c050d5e 100644
--- a/sound/soc/qcom/sdw.c
+++ b/sound/soc/qcom/sdw.c
@@ -41,6 +41,7 @@ static bool qcom_snd_is_sdw_dai(int id)
 	switch (id) {
 	case LPASS_CDC_DMA_TX3:
 	case LPASS_CDC_DMA_RX0:
+	case LPASS_CDC_DMA_WSA_RX0:
 		return true;
 	default:
 		break;

-- 
2.34.1


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

* [PATCH RFC 19/23] ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (17 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 18/23] ASoC: qcom: swd: allow LPASS_CDC_DMA_WSA_RX0 as soundwire dai Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:12   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi Neil Armstrong
                   ` (3 subsequent siblings)
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Add the missing DAI ids and set the ops to allow using
the sc8280xp card with lpass-cpu and keep the sm8650 sound
card bindings working with either Audioreach or lpass-cpu
interfaces.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 sound/soc/qcom/sc8280xp.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 8cc75a7ad866..ff57fb9051a2 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright (c) 2022, Linaro Limited
 
+#include <dt-bindings/sound/qcom,lpass.h>
 #include <dt-bindings/sound/qcom,q6afe.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
@@ -242,6 +243,8 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
 	switch (cpu_dai->id) {
 	case WSA_CODEC_DMA_RX_0:
 	case WSA_CODEC_DMA_RX_1:
+	case LPASS_CDC_DMA_WSA_RX0:
+	case LPASS_CDC_DMA_WSA_RX1:
 		/*
 		 * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume
 		 * to reduce the risk of speaker damage until we have active
@@ -323,6 +326,10 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 		}
 		break;
 	}
+	case LPASS_CDC_DMA_TX0:
+	case LPASS_CDC_DMA_TX1:
+	case LPASS_CDC_DMA_TX2:
+	case LPASS_CDC_DMA_TX3:
 	case TX_CODEC_DMA_TX_0:
 	case TX_CODEC_DMA_TX_1:
 	case TX_CODEC_DMA_TX_2:
@@ -533,12 +540,11 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
 	int i;
 
 	for_each_card_prelinks(card, i, link) {
-		if (link->no_pcm == 1) {
-			link->init = sc8280xp_snd_init;
-			link->exit = sc8280xp_snd_exit;
+		link->init = sc8280xp_snd_init;
+		link->exit = sc8280xp_snd_exit;
+		link->ops = &sc8280xp_be_ops;
+		if (link->no_pcm == 1)
 			link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup;
-			link->ops = &sc8280xp_be_ops;
-		}
 	}
 }
 

-- 
2.34.1


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

* [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (18 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 19/23] ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:08   ` Val Packett
  2026-11-13 19:11   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 21/23] arm64: dts: qcom: add SM8650 LPASS CPU dtsi Neil Armstrong
                   ` (2 subsequent siblings)
  22 siblings, 2 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

The reserved memory map, remoteprocs, SMP2P, IPA, GPU zap shader and the
q6prm LPASS clocks only apply when booting at EL1 under Gunyah, and
boards booting Linux at EL2 had to delete them.

Move them into sm8650-el1.dtsi, included by the EL1 boards, and drop the
node deletions from the Deckard dts.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 .../boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts      |   1 +
 arch/arm64/boot/dts/qcom/sm8650-el1.dtsi           | 775 +++++++++++++++++++++
 arch/arm64/boot/dts/qcom/sm8650-hdk.dts            |   1 +
 arch/arm64/boot/dts/qcom/sm8650-mtp.dts            |   1 +
 arch/arm64/boot/dts/qcom/sm8650-qrd.dts            |   1 +
 arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts  |  21 -
 arch/arm64/boot/dts/qcom/sm8650.dtsi               | 774 +-------------------
 7 files changed, 801 insertions(+), 773 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts b/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts
index 48fbf0650f0e..dfcd26bbe42c 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts
@@ -10,6 +10,7 @@
 #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include "sm8650.dtsi"
+#include "sm8650-el1.dtsi"
 #include "pm8550.dtsi"
 #include "pm8550b.dtsi"
 #define PMK8550VE_SID 8
diff --git a/arch/arm64/boot/dts/qcom/sm8650-el1.dtsi b/arch/arm64/boot/dts/qcom/sm8650-el1.dtsi
new file mode 100644
index 000000000000..18b7c18ae312
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sm8650-el1.dtsi
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2026, Linaro Limited
+ */
+
+#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+
+/ {
+	reserved_memory: reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		hyp_mem: hyp@80000000 {
+			reg = <0 0x80000000 0 0xe00000>;
+			no-map;
+		};
+
+		cpusys_vm_mem: cpusys-vm@80e00000 {
+			reg = <0 0x80e00000 0 0x400000>;
+			no-map;
+		};
+
+		/* Merged xbl_dtlog, xbl_ramdump and aop_image regions */
+		xbl_dt_log_merged_mem: xbl-dt-log-merged@81a00000 {
+			reg = <0 0x81a00000 0 0x260000>;
+			no-map;
+		};
+
+		aop_cmd_db_mem: aop-cmd-db@81c60000 {
+			compatible = "qcom,cmd-db";
+			reg = <0 0x81c60000 0 0x20000>;
+			no-map;
+		};
+
+		/* Merged aop_config, tme_crash_dump, tme_log, uefi_log, and chipinfo regions */
+		aop_tme_uefi_merged_mem: aop-tme-uefi-merged@81c80000 {
+			reg = <0 0x81c80000 0 0x75000>;
+			no-map;
+		};
+
+		/* Secdata region can be reused by apps */
+
+		smem: smem@81d00000 {
+			compatible = "qcom,smem";
+			reg = <0 0x81d00000 0 0x200000>;
+			hwlocks = <&tcsr_mutex 3>;
+			no-map;
+		};
+
+		adsp_mhi_mem: adsp-mhi@81f00000 {
+			reg = <0 0x81f00000 0 0x20000>;
+			no-map;
+		};
+
+		pvmfw_mem: pvmfw@824a0000 {
+			reg = <0 0x824a0000 0 0x100000>;
+			no-map;
+		};
+
+		global_sync_mem: global-sync@82600000 {
+			reg = <0 0x82600000 0 0x100000>;
+			no-map;
+		};
+
+		tz_stat_mem: tz-stat@82700000 {
+			reg = <0 0x82700000 0 0x100000>;
+			no-map;
+		};
+
+		qdss_mem: qdss@82800000 {
+			reg = <0 0x82800000 0 0x2000000>;
+			no-map;
+		};
+
+		qlink_logging_mem: qlink-logging@84800000 {
+			reg = <0 0x84800000 0 0x200000>;
+			no-map;
+		};
+
+		mpss_dsm_mem: mpss-dsm@86b00000 {
+			reg = <0 0x86b00000 0 0x4900000>;
+			no-map;
+		};
+
+		mpss_dsm_mem_2: mpss-dsm-2@8b400000 {
+			reg = <0 0x8b400000 0 0x800000>;
+			no-map;
+		};
+
+		mpss_mem: mpss@8bc00000 {
+			reg = <0 0x8bc00000 0 0xf400000>;
+			no-map;
+		};
+
+		q6_mpss_dtb_mem: q6-mpss-dtb@9b000000 {
+			reg = <0 0x9b000000 0 0x80000>;
+			no-map;
+		};
+
+		ipa_fw_mem: ipa-fw@9b080000 {
+			reg = <0 0x9b080000 0 0x10000>;
+			no-map;
+		};
+
+		ipa_gsi_mem: ipa-gsi@9b090000 {
+			reg = <0 0x9b090000 0 0xa000>;
+			no-map;
+		};
+
+		gpu_micro_code_mem: gpu-micro-code@9b09a000 {
+			reg = <0 0x9b09a000 0 0x2000>;
+			no-map;
+		};
+
+		spss_region_mem: spss@9b0a0000 {
+			reg = <0 0x9b0a0000 0 0x1e0000>;
+			no-map;
+		};
+
+		/* First part of the "SPU secure shared memory" region */
+		spu_tz_shared_mem: spu-tz-shared@9b280000 {
+			reg = <0 0x9b280000 0 0x60000>;
+			no-map;
+		};
+
+		/* Second part of the "SPU secure shared memory" region */
+		spu_modem_shared_mem: spu-modem-shared@9b2e0000 {
+			reg = <0 0x9b2e0000 0 0x20000>;
+			no-map;
+		};
+
+		camera_mem: camera@9b300000 {
+			reg = <0 0x9b300000 0 0x800000>;
+			no-map;
+		};
+
+		video_mem: video@9bb00000 {
+			reg = <0 0x9bb00000 0 0x800000>;
+			no-map;
+		};
+
+		cvp_mem: cvp@9c300000 {
+			reg = <0 0x9c300000 0 0x700000>;
+			no-map;
+		};
+
+		cdsp_mem: cdsp@9ca00000 {
+			reg = <0 0x9ca00000 0 0x1400000>;
+			no-map;
+		};
+
+		q6_cdsp_dtb_mem: q6-cdsp-dtb@9de00000 {
+			reg = <0 0x9de00000 0 0x80000>;
+			no-map;
+		};
+
+		q6_adsp_dtb_mem: q6-adsp-dtb@9de80000 {
+			reg = <0 0x9de80000 0 0x80000>;
+			no-map;
+		};
+
+		adspslpi_mem: adspslpi@9df00000 {
+			reg = <0 0x9df00000 0 0x4080000>;
+			no-map;
+		};
+
+		rmtfs_mem: rmtfs@d7c00000 {
+			compatible = "qcom,rmtfs-mem";
+			reg = <0 0xd7c00000 0 0x400000>;
+			no-map;
+
+			qcom,client-id = <1>;
+			qcom,vmid = <QCOM_SCM_VMID_MSS_MSA>;
+		};
+
+		/* Merged tz_reserved, xbl_sc, cpucp_fw and qtee regions */
+		tz_merged_mem: tz-merged@d8000000 {
+			reg = <0 0xd8000000 0 0x800000>;
+			no-map;
+		};
+
+		hwfence_shbuf: hwfence-shbuf@e6440000 {
+			reg = <0 0xe6440000 0 0x2dd000>;
+			no-map;
+		};
+
+		trust_ui_vm_mem: trust-ui-vm@f3800000 {
+			reg = <0 0xf3800000 0 0x4400000>;
+			no-map;
+		};
+
+		oem_vm_mem: oem-vm@f7c00000 {
+			reg = <0 0xf7c00000 0 0x4c00000>;
+			no-map;
+		};
+
+		llcc_lpi_mem: llcc-lpi@ff800000 {
+			reg = <0 0xff800000 0 0x600000>;
+			no-map;
+		};
+	};
+
+	smp2p_adsp: smp2p-adsp {
+		compatible = "qcom,smp2p";
+
+		interrupts-extended = <&ipcc IPCC_CLIENT_LPASS
+					     IPCC_MPROC_SIGNAL_SMP2P
+					     IRQ_TYPE_EDGE_RISING>;
+
+		mboxes = <&ipcc IPCC_CLIENT_LPASS
+				IPCC_MPROC_SIGNAL_SMP2P>;
+
+		qcom,smem = <443>, <429>;
+		qcom,local-pid = <0>;
+		qcom,remote-pid = <2>;
+
+		smp2p_adsp_out: master-kernel {
+			qcom,entry-name = "master-kernel";
+			#qcom,smem-state-cells = <1>;
+		};
+
+		smp2p_adsp_in: slave-kernel {
+			qcom,entry-name = "slave-kernel";
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+	};
+
+	smp2p_cdsp: smp2p-cdsp {
+		compatible = "qcom,smp2p";
+
+		interrupts-extended = <&ipcc IPCC_CLIENT_CDSP
+					     IPCC_MPROC_SIGNAL_SMP2P
+					     IRQ_TYPE_EDGE_RISING>;
+
+		mboxes = <&ipcc IPCC_CLIENT_CDSP
+				IPCC_MPROC_SIGNAL_SMP2P>;
+
+		qcom,smem = <94>, <432>;
+		qcom,local-pid = <0>;
+		qcom,remote-pid = <5>;
+
+		smp2p_cdsp_out: master-kernel {
+			qcom,entry-name = "master-kernel";
+			#qcom,smem-state-cells = <1>;
+		};
+
+		smp2p_cdsp_in: slave-kernel {
+			qcom,entry-name = "slave-kernel";
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+	};
+
+	smp2p_modem: smp2p-modem {
+		compatible = "qcom,smp2p";
+
+		interrupts-extended = <&ipcc IPCC_CLIENT_MPSS
+					     IPCC_MPROC_SIGNAL_SMP2P
+					     IRQ_TYPE_EDGE_RISING>;
+
+		mboxes = <&ipcc IPCC_CLIENT_MPSS
+				IPCC_MPROC_SIGNAL_SMP2P>;
+
+		qcom,smem = <435>, <428>;
+		qcom,local-pid = <0>;
+		qcom,remote-pid = <1>;
+
+		smp2p_modem_out: master-kernel {
+			qcom,entry-name = "master-kernel";
+			#qcom,smem-state-cells = <1>;
+		};
+
+		smp2p_modem_in: slave-kernel {
+			qcom,entry-name = "slave-kernel";
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		ipa_smp2p_out: ipa-ap-to-modem {
+			qcom,entry-name = "ipa";
+			#qcom,smem-state-cells = <1>;
+		};
+
+		ipa_smp2p_in: ipa-modem-to-ap {
+			qcom,entry-name = "ipa";
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+	};
+};
+
+&gpu {
+	gpu_zap_shader: zap-shader {
+		memory-region = <&gpu_micro_code_mem>;
+	};
+};
+
+&iris {
+	memory-region = <&video_mem>;
+};
+
+&lpass_rxmacro {
+	clocks = <&q6prmcc LPASS_CLK_ID_RX_CORE_TX_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&lpass_vamacro>;
+	status = "okay";
+};
+
+&lpass_tlmm {
+	clocks = <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+	status = "okay";
+};
+
+&lpass_txmacro {
+	clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&lpass_vamacro>;
+	status = "okay";
+};
+
+&lpass_vamacro {
+	clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+	clock-names = "mclk",
+		      "macro",
+		      "dcodec";
+	status = "okay";
+};
+
+&lpass_wsa2macro {
+	clocks = <&q6prmcc LPASS_CLK_ID_WSA2_CORE_TX_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&lpass_vamacro>;
+	status = "okay";
+};
+
+&lpass_wsamacro {
+	clocks = <&q6prmcc LPASS_CLK_ID_WSA_CORE_TX_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+		 <&lpass_vamacro>;
+	status = "okay";
+};
+
+&soc {
+	ipa: ipa@3f40000 {
+		compatible = "qcom,sm8650-ipa", "qcom,sm8550-ipa";
+
+		iommus = <&apps_smmu 0x4a0 0x0>,
+			 <&apps_smmu 0x4a2 0x0>;
+		reg = <0 0x3f40000 0 0x10000>,
+		      <0 0x3f50000 0 0x5000>,
+		      <0 0x3e04000 0 0xfc000>;
+		reg-names = "ipa-reg",
+			    "ipa-shared",
+			    "gsi";
+
+		interrupts-extended = <&intc GIC_SPI 654 IRQ_TYPE_EDGE_RISING 0>,
+				      <&intc GIC_SPI 432 IRQ_TYPE_LEVEL_HIGH 0>,
+				      <&ipa_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
+				      <&ipa_smp2p_in 1 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "ipa",
+				  "gsi",
+				  "ipa-clock-query",
+				  "ipa-setup-ready";
+
+		clocks = <&rpmhcc RPMH_IPA_CLK>;
+		clock-names = "core";
+
+		interconnects = <&aggre2_noc MASTER_IPA QCOM_ICC_TAG_ALWAYS
+				 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
+				<&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
+				 &config_noc SLAVE_IPA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
+		interconnect-names = "memory",
+				     "config";
+
+		qcom,qmp = <&aoss_qmp>;
+
+		qcom,smem-states = <&ipa_smp2p_out 0>,
+				   <&ipa_smp2p_out 1>;
+		qcom,smem-state-names = "ipa-clock-enabled-valid",
+					"ipa-clock-enabled";
+
+		qcom,gsi-loader = "self";
+		memory-region = <&ipa_fw_mem>;
+
+		sram = <&ipa_modem_tables>;
+
+		status = "disabled";
+	};
+
+	remoteproc_mpss: remoteproc@4080000 {
+		compatible = "qcom,sm8650-mpss-pas";
+		reg = <0x0 0x04080000 0x0 0x10000>;
+
+		interrupts-extended = <&intc GIC_SPI 264 IRQ_TYPE_EDGE_RISING 0>,
+				      <&smp2p_modem_in 0 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_modem_in 1 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_modem_in 2 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_modem_in 3 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_modem_in 7 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "wdog",
+				  "fatal",
+				  "ready",
+				  "handover",
+				  "stop-ack",
+				  "shutdown-ack";
+
+		clocks = <&rpmhcc RPMH_CXO_CLK>;
+		clock-names = "xo";
+
+		interconnects = <&mc_virt MASTER_LLCC QCOM_ICC_TAG_ALWAYS
+				 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+
+		power-domains = <&rpmhpd RPMHPD_CX>,
+				<&rpmhpd RPMHPD_MSS>;
+		power-domain-names = "cx",
+				     "mss";
+
+		memory-region = <&mpss_mem>, <&q6_mpss_dtb_mem>,
+				<&mpss_dsm_mem>, <&mpss_dsm_mem_2>,
+				<&qlink_logging_mem>;
+
+		qcom,qmp = <&aoss_qmp>;
+
+		qcom,smem-states = <&smp2p_modem_out 0>;
+		qcom,smem-state-names = "stop";
+
+		status = "disabled";
+
+		glink-edge {
+			interrupts-extended = <&ipcc IPCC_CLIENT_MPSS
+						     IPCC_MPROC_SIGNAL_GLINK_QMP
+						     IRQ_TYPE_EDGE_RISING>;
+
+			mboxes = <&ipcc IPCC_CLIENT_MPSS
+					IPCC_MPROC_SIGNAL_GLINK_QMP>;
+
+			qcom,remote-pid = <1>;
+
+			label = "mpss";
+		};
+	};
+
+	remoteproc_adsp: remoteproc@6800000 {
+		compatible = "qcom,sm8650-adsp-pas";
+		reg = <0x0 0x06800000 0x0 0x10000>;
+
+		interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_adsp_in 1 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_adsp_in 2 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_adsp_in 3 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "wdog",
+				  "fatal",
+				  "ready",
+				  "handover",
+				  "stop-ack";
+
+		clocks = <&rpmhcc RPMH_CXO_CLK>;
+		clock-names = "xo";
+
+		interconnects = <&lpass_lpicx_noc MASTER_LPASS_PROC QCOM_ICC_TAG_ALWAYS
+				 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+
+		power-domains = <&rpmhpd RPMHPD_LCX>,
+				<&rpmhpd RPMHPD_LMX>;
+		power-domain-names = "lcx",
+				     "lmx";
+
+		memory-region = <&adspslpi_mem>, <&q6_adsp_dtb_mem>;
+
+		qcom,qmp = <&aoss_qmp>;
+
+		qcom,smem-states = <&smp2p_adsp_out 0>;
+		qcom,smem-state-names = "stop";
+
+		status = "disabled";
+
+		glink-edge {
+			interrupts-extended = <&ipcc IPCC_CLIENT_LPASS
+						     IPCC_MPROC_SIGNAL_GLINK_QMP
+						     IRQ_TYPE_EDGE_RISING>;
+
+			mboxes = <&ipcc IPCC_CLIENT_LPASS
+					IPCC_MPROC_SIGNAL_GLINK_QMP>;
+
+			qcom,remote-pid = <2>;
+
+			label = "lpass";
+
+			fastrpc {
+				compatible = "qcom,fastrpc";
+
+				qcom,glink-channels = "fastrpcglink-apps-dsp";
+
+				label = "adsp";
+
+				qcom,non-secure-domain;
+
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				compute-cb@3 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <3>;
+
+					iommus = <&apps_smmu 0x1003 0x80>,
+						 <&apps_smmu 0x1043 0x20>;
+					dma-coherent;
+				};
+
+				compute-cb@4 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <4>;
+
+					iommus = <&apps_smmu 0x1004 0x80>,
+						 <&apps_smmu 0x1044 0x20>;
+					dma-coherent;
+				};
+
+				compute-cb@5 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <5>;
+
+					iommus = <&apps_smmu 0x1005 0x80>,
+						 <&apps_smmu 0x1045 0x20>;
+					dma-coherent;
+				};
+
+				compute-cb@6 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <6>;
+
+					iommus = <&apps_smmu 0x1006 0x80>,
+						 <&apps_smmu 0x1046 0x20>;
+					dma-coherent;
+				};
+
+				compute-cb@7 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <7>;
+
+					iommus = <&apps_smmu 0x1007 0x40>,
+						 <&apps_smmu 0x1067 0x0>,
+						 <&apps_smmu 0x1087 0x0>;
+					dma-coherent;
+				};
+			};
+
+			gpr {
+				compatible = "qcom,gpr";
+				qcom,glink-channels = "adsp_apps";
+				qcom,domain = <GPR_DOMAIN_ID_ADSP>;
+				qcom,intents = <512 20>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				q6apm: service@1 {
+					compatible = "qcom,q6apm";
+					reg = <GPR_APM_MODULE_IID>;
+					#sound-dai-cells = <0>;
+					qcom,protection-domain = "avs/audio",
+								 "msm/adsp/audio_pd";
+
+					q6apmbedai: bedais {
+						compatible = "qcom,q6apm-lpass-dais";
+						#sound-dai-cells = <1>;
+					};
+
+					q6apmdai: dais {
+						compatible = "qcom,q6apm-dais";
+						iommus = <&apps_smmu 0x1001 0x80>,
+							 <&apps_smmu 0x1061 0x0>;
+					};
+				};
+
+				q6prm: service@2 {
+					compatible = "qcom,q6prm";
+					reg = <GPR_PRM_MODULE_IID>;
+					qcom,protection-domain = "avs/audio",
+								 "msm/adsp/audio_pd";
+
+					q6prmcc: clock-controller {
+						compatible = "qcom,q6prm-lpass-clocks";
+						#clock-cells = <2>;
+					};
+				};
+			};
+		};
+	};
+
+	remoteproc_cdsp: remoteproc@32300000 {
+		compatible = "qcom,sm8650-cdsp-pas";
+		reg = <0x0 0x32300000 0x0 0x10000>;
+
+		interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING 0>,
+				      <&smp2p_cdsp_in 0 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_cdsp_in 1 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_cdsp_in 2 IRQ_TYPE_EDGE_RISING>,
+				      <&smp2p_cdsp_in 3 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "wdog",
+				  "fatal",
+				  "ready",
+				  "handover",
+				  "stop-ack";
+
+		clocks = <&rpmhcc RPMH_CXO_CLK>;
+		clock-names = "xo";
+
+		interconnects = <&nsp_noc MASTER_CDSP_PROC QCOM_ICC_TAG_ALWAYS
+				 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+
+		power-domains = <&rpmhpd RPMHPD_CX>,
+				<&rpmhpd RPMHPD_MXC>,
+				<&rpmhpd RPMHPD_NSP>;
+		power-domain-names = "cx",
+				     "mxc",
+				     "nsp";
+
+		memory-region = <&cdsp_mem>, <&q6_cdsp_dtb_mem>, <&global_sync_mem>;
+
+		qcom,qmp = <&aoss_qmp>;
+
+		qcom,smem-states = <&smp2p_cdsp_out 0>;
+		qcom,smem-state-names = "stop";
+
+		status = "disabled";
+
+		glink-edge {
+			interrupts-extended = <&ipcc IPCC_CLIENT_CDSP
+						     IPCC_MPROC_SIGNAL_GLINK_QMP
+						     IRQ_TYPE_EDGE_RISING>;
+
+			mboxes = <&ipcc IPCC_CLIENT_CDSP
+					IPCC_MPROC_SIGNAL_GLINK_QMP>;
+
+			qcom,remote-pid = <5>;
+
+			label = "cdsp";
+
+			fastrpc {
+				compatible = "qcom,fastrpc";
+
+				qcom,glink-channels = "fastrpcglink-apps-dsp";
+
+				label = "cdsp";
+
+				qcom,non-secure-domain;
+
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				compute-cb@1 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <1>;
+
+					iommus = <&apps_smmu 0x1961 0x0>,
+						 <&apps_smmu 0x0c01 0x20>,
+						 <&apps_smmu 0x19c1 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@2 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <2>;
+
+					iommus = <&apps_smmu 0x1962 0x0>,
+						 <&apps_smmu 0x0c02 0x20>,
+						 <&apps_smmu 0x19c2 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@3 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <3>;
+
+					iommus = <&apps_smmu 0x1963 0x0>,
+						 <&apps_smmu 0x0c03 0x20>,
+						 <&apps_smmu 0x19c3 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@4 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <4>;
+
+					iommus = <&apps_smmu 0x1964 0x0>,
+						 <&apps_smmu 0x0c04 0x20>,
+						 <&apps_smmu 0x19c4 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@5 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <5>;
+
+					iommus = <&apps_smmu 0x1965 0x0>,
+						 <&apps_smmu 0x0c05 0x20>,
+						 <&apps_smmu 0x19c5 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@6 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <6>;
+
+					iommus = <&apps_smmu 0x1966 0x0>,
+						 <&apps_smmu 0x0c06 0x20>,
+						 <&apps_smmu 0x19c6 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@7 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <7>;
+
+					iommus = <&apps_smmu 0x1967 0x0>,
+						 <&apps_smmu 0x0c07 0x20>,
+						 <&apps_smmu 0x19c7 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@8 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <8>;
+
+					iommus = <&apps_smmu 0x1968 0x0>,
+						 <&apps_smmu 0x0c08 0x20>,
+						 <&apps_smmu 0x19c8 0x0>;
+					dma-coherent;
+				};
+
+				/* note: secure cb9 in downstream */
+
+				compute-cb@12 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <12>;
+
+					iommus = <&apps_smmu 0x196c 0x0>,
+						 <&apps_smmu 0x0c0c 0x20>,
+						 <&apps_smmu 0x19cc 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@13 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <13>;
+
+					iommus = <&apps_smmu 0x196d 0x0>,
+						 <&apps_smmu 0x0c0d 0x20>,
+						 <&apps_smmu 0x19cd 0x0>;
+					dma-coherent;
+				};
+
+				compute-cb@14 {
+					compatible = "qcom,fastrpc-compute-cb";
+					reg = <14>;
+
+					iommus = <&apps_smmu 0x196e 0x0>,
+						 <&apps_smmu 0x0c0e 0x20>,
+						 <&apps_smmu 0x19ce 0x0>;
+					dma-coherent;
+				};
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sm8650-hdk.dts b/arch/arm64/boot/dts/qcom/sm8650-hdk.dts
index 6cb0dc089b17..70eb59989ed1 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-hdk.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-hdk.dts
@@ -8,6 +8,7 @@
 #include <dt-bindings/leds/common.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include "sm8650.dtsi"
+#include "sm8650-el1.dtsi"
 #include "pm8010.dtsi"
 #include "pm8550.dtsi"
 #include "pm8550b.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sm8650-mtp.dts b/arch/arm64/boot/dts/qcom/sm8650-mtp.dts
index f3ed42f81b0b..9f8ad184a82a 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-mtp.dts
@@ -7,6 +7,7 @@
 
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include "sm8650.dtsi"
+#include "sm8650-el1.dtsi"
 #include "pm8010.dtsi"
 #include "pm8550.dtsi"
 #include "pm8550b.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sm8650-qrd.dts b/arch/arm64/boot/dts/qcom/sm8650-qrd.dts
index f693ea6a5461..e22f8db3027f 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-qrd.dts
@@ -8,6 +8,7 @@
 #include <dt-bindings/leds/common.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include "sm8650.dtsi"
+#include "sm8650-el1.dtsi"
 #include "pm8010.dtsi"
 #include "pm8550.dtsi"
 #include "pm8550b.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts b/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
index d25a09e162dd..a365caa28cee 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
@@ -17,27 +17,6 @@
 #include "pm8550vs.dtsi"
 #include "pmk8550.dtsi"
 
-/* Remove nodes */
-/delete-node/ &ipa;
-/delete-node/ &gpu_zap_shader;
-/delete-node/ &lpass_wsa2macro;
-/delete-node/ &lpass_rxmacro;
-/delete-node/ &lpass_txmacro;
-/delete-node/ &lpass_wsamacro;
-/delete-node/ &lpass_vamacro;
-/delete-node/ &lpass_tlmm;
-/delete-node/ &remoteproc_adsp;
-/delete-node/ &remoteproc_cdsp;
-/delete-node/ &remoteproc_mpss;
-/delete-node/ &reserved_memory;
-/delete-node/ &smp2p_adsp;
-/delete-node/ &smp2p_cdsp;
-/delete-node/ &smp2p_modem;
-/delete-node/ &swr0;
-/delete-node/ &swr1;
-/delete-node/ &swr2;
-/delete-node/ &swr3;
-
 / {
 	model = "Valve Deckard";
 	compatible = "valve,deckard", "qcom,sm8650";
diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi
index 86fbb0fd6472..d88acdb8c775 100644
--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi
@@ -24,7 +24,6 @@
 #include <dt-bindings/reset/qcom,sm8650-gpucc.h>
 #include <dt-bindings/soc/qcom,gpr.h>
 #include <dt-bindings/soc/qcom,rpmh-rsc.h>
-#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -1711,290 +1710,6 @@ cluster_pd: power-domain-cluster {
 		};
 	};
 
-	reserved_memory: reserved-memory {
-		#address-cells = <2>;
-		#size-cells = <2>;
-		ranges;
-
-		hyp_mem: hyp@80000000 {
-			reg = <0 0x80000000 0 0xe00000>;
-			no-map;
-		};
-
-		cpusys_vm_mem: cpusys-vm@80e00000 {
-			reg = <0 0x80e00000 0 0x400000>;
-			no-map;
-		};
-
-		/* Merged xbl_dtlog, xbl_ramdump and aop_image regions */
-		xbl_dt_log_merged_mem: xbl-dt-log-merged@81a00000 {
-			reg = <0 0x81a00000 0 0x260000>;
-			no-map;
-		};
-
-		aop_cmd_db_mem: aop-cmd-db@81c60000 {
-			compatible = "qcom,cmd-db";
-			reg = <0 0x81c60000 0 0x20000>;
-			no-map;
-		};
-
-		/* Merged aop_config, tme_crash_dump, tme_log, uefi_log, and chipinfo regions */
-		aop_tme_uefi_merged_mem: aop-tme-uefi-merged@81c80000 {
-			reg = <0 0x81c80000 0 0x75000>;
-			no-map;
-		};
-
-		/* Secdata region can be reused by apps */
-
-		smem: smem@81d00000 {
-			compatible = "qcom,smem";
-			reg = <0 0x81d00000 0 0x200000>;
-			hwlocks = <&tcsr_mutex 3>;
-			no-map;
-		};
-
-		adsp_mhi_mem: adsp-mhi@81f00000 {
-			reg = <0 0x81f00000 0 0x20000>;
-			no-map;
-		};
-
-		pvmfw_mem: pvmfw@824a0000 {
-			reg = <0 0x824a0000 0 0x100000>;
-			no-map;
-		};
-
-		global_sync_mem: global-sync@82600000 {
-			reg = <0 0x82600000 0 0x100000>;
-			no-map;
-		};
-
-		tz_stat_mem: tz-stat@82700000 {
-			reg = <0 0x82700000 0 0x100000>;
-			no-map;
-		};
-
-		qdss_mem: qdss@82800000 {
-			reg = <0 0x82800000 0 0x2000000>;
-			no-map;
-		};
-
-		qlink_logging_mem: qlink-logging@84800000 {
-			reg = <0 0x84800000 0 0x200000>;
-			no-map;
-		};
-
-		mpss_dsm_mem: mpss-dsm@86b00000 {
-			reg = <0 0x86b00000 0 0x4900000>;
-			no-map;
-		};
-
-		mpss_dsm_mem_2: mpss-dsm-2@8b400000 {
-			reg = <0 0x8b400000 0 0x800000>;
-			no-map;
-		};
-
-		mpss_mem: mpss@8bc00000 {
-			reg = <0 0x8bc00000 0 0xf400000>;
-			no-map;
-		};
-
-		q6_mpss_dtb_mem: q6-mpss-dtb@9b000000 {
-			reg = <0 0x9b000000 0 0x80000>;
-			no-map;
-		};
-
-		ipa_fw_mem: ipa-fw@9b080000 {
-			reg = <0 0x9b080000 0 0x10000>;
-			no-map;
-		};
-
-		ipa_gsi_mem: ipa-gsi@9b090000 {
-			reg = <0 0x9b090000 0 0xa000>;
-			no-map;
-		};
-
-		gpu_micro_code_mem: gpu-micro-code@9b09a000 {
-			reg = <0 0x9b09a000 0 0x2000>;
-			no-map;
-		};
-
-		spss_region_mem: spss@9b0a0000 {
-			reg = <0 0x9b0a0000 0 0x1e0000>;
-			no-map;
-		};
-
-		/* First part of the "SPU secure shared memory" region */
-		spu_tz_shared_mem: spu-tz-shared@9b280000 {
-			reg = <0 0x9b280000 0 0x60000>;
-			no-map;
-		};
-
-		/* Second part of the "SPU secure shared memory" region */
-		spu_modem_shared_mem: spu-modem-shared@9b2e0000 {
-			reg = <0 0x9b2e0000 0 0x20000>;
-			no-map;
-		};
-
-		camera_mem: camera@9b300000 {
-			reg = <0 0x9b300000 0 0x800000>;
-			no-map;
-		};
-
-		video_mem: video@9bb00000 {
-			reg = <0 0x9bb00000 0 0x800000>;
-			no-map;
-		};
-
-		cvp_mem: cvp@9c300000 {
-			reg = <0 0x9c300000 0 0x700000>;
-			no-map;
-		};
-
-		cdsp_mem: cdsp@9ca00000 {
-			reg = <0 0x9ca00000 0 0x1400000>;
-			no-map;
-		};
-
-		q6_cdsp_dtb_mem: q6-cdsp-dtb@9de00000 {
-			reg = <0 0x9de00000 0 0x80000>;
-			no-map;
-		};
-
-		q6_adsp_dtb_mem: q6-adsp-dtb@9de80000 {
-			reg = <0 0x9de80000 0 0x80000>;
-			no-map;
-		};
-
-		adspslpi_mem: adspslpi@9df00000 {
-			reg = <0 0x9df00000 0 0x4080000>;
-			no-map;
-		};
-
-		rmtfs_mem: rmtfs@d7c00000 {
-			compatible = "qcom,rmtfs-mem";
-			reg = <0 0xd7c00000 0 0x400000>;
-			no-map;
-
-			qcom,client-id = <1>;
-			qcom,vmid = <QCOM_SCM_VMID_MSS_MSA>;
-		};
-
-		/* Merged tz_reserved, xbl_sc, cpucp_fw and qtee regions */
-		tz_merged_mem: tz-merged@d8000000 {
-			reg = <0 0xd8000000 0 0x800000>;
-			no-map;
-		};
-
-		hwfence_shbuf: hwfence-shbuf@e6440000 {
-			reg = <0 0xe6440000 0 0x2dd000>;
-			no-map;
-		};
-
-		trust_ui_vm_mem: trust-ui-vm@f3800000 {
-			reg = <0 0xf3800000 0 0x4400000>;
-			no-map;
-		};
-
-		oem_vm_mem: oem-vm@f7c00000 {
-			reg = <0 0xf7c00000 0 0x4c00000>;
-			no-map;
-		};
-
-		llcc_lpi_mem: llcc-lpi@ff800000 {
-			reg = <0 0xff800000 0 0x600000>;
-			no-map;
-		};
-	};
-
-	smp2p_adsp: smp2p-adsp {
-		compatible = "qcom,smp2p";
-
-		interrupts-extended = <&ipcc IPCC_CLIENT_LPASS
-					     IPCC_MPROC_SIGNAL_SMP2P
-					     IRQ_TYPE_EDGE_RISING>;
-
-		mboxes = <&ipcc IPCC_CLIENT_LPASS
-				IPCC_MPROC_SIGNAL_SMP2P>;
-
-		qcom,smem = <443>, <429>;
-		qcom,local-pid = <0>;
-		qcom,remote-pid = <2>;
-
-		smp2p_adsp_out: master-kernel {
-			qcom,entry-name = "master-kernel";
-			#qcom,smem-state-cells = <1>;
-		};
-
-		smp2p_adsp_in: slave-kernel {
-			qcom,entry-name = "slave-kernel";
-			interrupt-controller;
-			#interrupt-cells = <2>;
-		};
-	};
-
-	smp2p_cdsp: smp2p-cdsp {
-		compatible = "qcom,smp2p";
-
-		interrupts-extended = <&ipcc IPCC_CLIENT_CDSP
-					     IPCC_MPROC_SIGNAL_SMP2P
-					     IRQ_TYPE_EDGE_RISING>;
-
-		mboxes = <&ipcc IPCC_CLIENT_CDSP
-				IPCC_MPROC_SIGNAL_SMP2P>;
-
-		qcom,smem = <94>, <432>;
-		qcom,local-pid = <0>;
-		qcom,remote-pid = <5>;
-
-		smp2p_cdsp_out: master-kernel {
-			qcom,entry-name = "master-kernel";
-			#qcom,smem-state-cells = <1>;
-		};
-
-		smp2p_cdsp_in: slave-kernel {
-			qcom,entry-name = "slave-kernel";
-			interrupt-controller;
-			#interrupt-cells = <2>;
-		};
-	};
-
-	smp2p_modem: smp2p-modem {
-		compatible = "qcom,smp2p";
-
-		interrupts-extended = <&ipcc IPCC_CLIENT_MPSS
-					     IPCC_MPROC_SIGNAL_SMP2P
-					     IRQ_TYPE_EDGE_RISING>;
-
-		mboxes = <&ipcc IPCC_CLIENT_MPSS
-				IPCC_MPROC_SIGNAL_SMP2P>;
-
-		qcom,smem = <435>, <428>;
-		qcom,local-pid = <0>;
-		qcom,remote-pid = <1>;
-
-		smp2p_modem_out: master-kernel {
-			qcom,entry-name = "master-kernel";
-			#qcom,smem-state-cells = <1>;
-		};
-
-		smp2p_modem_in: slave-kernel {
-			qcom,entry-name = "slave-kernel";
-			interrupt-controller;
-			#interrupt-cells = <2>;
-		};
-
-		ipa_smp2p_out: ipa-ap-to-modem {
-			qcom,entry-name = "ipa";
-			#qcom,smem-state-cells = <1>;
-		};
-
-		ipa_smp2p_in: ipa-modem-to-ap {
-			qcom,entry-name = "ipa";
-			interrupt-controller;
-			#interrupt-cells = <2>;
-		};
-	};
-
 	soc: soc@0 {
 		compatible = "simple-bus";
 
@@ -4217,10 +3932,6 @@ gpu: gpu@3d00000 {
 
 			status = "disabled";
 
-			gpu_zap_shader: zap-shader {
-				memory-region = <&gpu_micro_code_mem>;
-			};
-
 			/* Speedbin needs more work on A740+, keep only lower freqs */
 			gpu_opp_table: opp-table {
 				compatible = "operating-points-v2-adreno",
@@ -4414,268 +4125,17 @@ adreno_smmu: iommu@3da0000 {
 			dma-coherent;
 		};
 
-		ipa: ipa@3f40000 {
-			compatible = "qcom,sm8650-ipa", "qcom,sm8550-ipa";
-
-			iommus = <&apps_smmu 0x4a0 0x0>,
-				 <&apps_smmu 0x4a2 0x0>;
-			reg = <0 0x3f40000 0 0x10000>,
-			      <0 0x3f50000 0 0x5000>,
-			      <0 0x3e04000 0 0xfc000>;
-			reg-names = "ipa-reg",
-				    "ipa-shared",
-				    "gsi";
-
-			interrupts-extended = <&intc GIC_SPI 654 IRQ_TYPE_EDGE_RISING 0>,
-					      <&intc GIC_SPI 432 IRQ_TYPE_LEVEL_HIGH 0>,
-					      <&ipa_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
-					      <&ipa_smp2p_in 1 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "ipa",
-					  "gsi",
-					  "ipa-clock-query",
-					  "ipa-setup-ready";
-
-			clocks = <&rpmhcc RPMH_IPA_CLK>;
-			clock-names = "core";
-
-			interconnects = <&aggre2_noc MASTER_IPA QCOM_ICC_TAG_ALWAYS
-					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
-					<&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
-					 &config_noc SLAVE_IPA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
-			interconnect-names = "memory",
-					     "config";
-
-			qcom,qmp = <&aoss_qmp>;
-
-			qcom,smem-states = <&ipa_smp2p_out 0>,
-					   <&ipa_smp2p_out 1>;
-			qcom,smem-state-names = "ipa-clock-enabled-valid",
-						"ipa-clock-enabled";
-
-			qcom,gsi-loader = "self";
-			memory-region = <&ipa_fw_mem>;
-
-			sram = <&ipa_modem_tables>;
-
-			status = "disabled";
-		};
-
-		remoteproc_mpss: remoteproc@4080000 {
-			compatible = "qcom,sm8650-mpss-pas";
-			reg = <0x0 0x04080000 0x0 0x10000>;
-
-			interrupts-extended = <&intc GIC_SPI 264 IRQ_TYPE_EDGE_RISING 0>,
-					      <&smp2p_modem_in 0 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_modem_in 1 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_modem_in 2 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_modem_in 3 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_modem_in 7 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "wdog",
-					  "fatal",
-					  "ready",
-					  "handover",
-					  "stop-ack",
-					  "shutdown-ack";
-
-			clocks = <&rpmhcc RPMH_CXO_CLK>;
-			clock-names = "xo";
-
-			interconnects = <&mc_virt MASTER_LLCC QCOM_ICC_TAG_ALWAYS
-					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
-
-			power-domains = <&rpmhpd RPMHPD_CX>,
-					<&rpmhpd RPMHPD_MSS>;
-			power-domain-names = "cx",
-					     "mss";
-
-			memory-region = <&mpss_mem>, <&q6_mpss_dtb_mem>,
-					<&mpss_dsm_mem>, <&mpss_dsm_mem_2>,
-					<&qlink_logging_mem>;
-
-			qcom,qmp = <&aoss_qmp>;
-
-			qcom,smem-states = <&smp2p_modem_out 0>;
-			qcom,smem-state-names = "stop";
-
-			status = "disabled";
-
-			glink-edge {
-				interrupts-extended = <&ipcc IPCC_CLIENT_MPSS
-							     IPCC_MPROC_SIGNAL_GLINK_QMP
-							     IRQ_TYPE_EDGE_RISING>;
-
-				mboxes = <&ipcc IPCC_CLIENT_MPSS
-						IPCC_MPROC_SIGNAL_GLINK_QMP>;
-
-				qcom,remote-pid = <1>;
-
-				label = "mpss";
-			};
-		};
-
-		remoteproc_adsp: remoteproc@6800000 {
-			compatible = "qcom,sm8650-adsp-pas";
-			reg = <0x0 0x06800000 0x0 0x10000>;
-
-			interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_adsp_in 1 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_adsp_in 2 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_adsp_in 3 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "wdog",
-					  "fatal",
-					  "ready",
-					  "handover",
-					  "stop-ack";
-
-			clocks = <&rpmhcc RPMH_CXO_CLK>;
-			clock-names = "xo";
-
-			interconnects = <&lpass_lpicx_noc MASTER_LPASS_PROC QCOM_ICC_TAG_ALWAYS
-					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
-
-			power-domains = <&rpmhpd RPMHPD_LCX>,
-					<&rpmhpd RPMHPD_LMX>;
-			power-domain-names = "lcx",
-					     "lmx";
-
-			memory-region = <&adspslpi_mem>, <&q6_adsp_dtb_mem>;
-
-			qcom,qmp = <&aoss_qmp>;
-
-			qcom,smem-states = <&smp2p_adsp_out 0>;
-			qcom,smem-state-names = "stop";
-
-			status = "disabled";
-
-			glink-edge {
-				interrupts-extended = <&ipcc IPCC_CLIENT_LPASS
-							     IPCC_MPROC_SIGNAL_GLINK_QMP
-							     IRQ_TYPE_EDGE_RISING>;
-
-				mboxes = <&ipcc IPCC_CLIENT_LPASS
-						IPCC_MPROC_SIGNAL_GLINK_QMP>;
-
-				qcom,remote-pid = <2>;
-
-				label = "lpass";
-
-				fastrpc {
-					compatible = "qcom,fastrpc";
-
-					qcom,glink-channels = "fastrpcglink-apps-dsp";
-
-					label = "adsp";
-
-					qcom,non-secure-domain;
-
-					#address-cells = <1>;
-					#size-cells = <0>;
-
-					compute-cb@3 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <3>;
-
-						iommus = <&apps_smmu 0x1003 0x80>,
-							 <&apps_smmu 0x1043 0x20>;
-						dma-coherent;
-					};
-
-					compute-cb@4 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <4>;
-
-						iommus = <&apps_smmu 0x1004 0x80>,
-							 <&apps_smmu 0x1044 0x20>;
-						dma-coherent;
-					};
-
-					compute-cb@5 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <5>;
-
-						iommus = <&apps_smmu 0x1005 0x80>,
-							 <&apps_smmu 0x1045 0x20>;
-						dma-coherent;
-					};
-
-					compute-cb@6 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <6>;
-
-						iommus = <&apps_smmu 0x1006 0x80>,
-							 <&apps_smmu 0x1046 0x20>;
-						dma-coherent;
-					};
-
-					compute-cb@7 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <7>;
-
-						iommus = <&apps_smmu 0x1007 0x40>,
-							 <&apps_smmu 0x1067 0x0>,
-							 <&apps_smmu 0x1087 0x0>;
-						dma-coherent;
-					};
-				};
-
-				gpr {
-					compatible = "qcom,gpr";
-					qcom,glink-channels = "adsp_apps";
-					qcom,domain = <GPR_DOMAIN_ID_ADSP>;
-					qcom,intents = <512 20>;
-					#address-cells = <1>;
-					#size-cells = <0>;
-
-					q6apm: service@1 {
-						compatible = "qcom,q6apm";
-						reg = <GPR_APM_MODULE_IID>;
-						#sound-dai-cells = <0>;
-						qcom,protection-domain = "avs/audio",
-									 "msm/adsp/audio_pd";
-
-						q6apmbedai: bedais {
-							compatible = "qcom,q6apm-lpass-dais";
-							#sound-dai-cells = <1>;
-						};
-
-						q6apmdai: dais {
-							compatible = "qcom,q6apm-dais";
-							iommus = <&apps_smmu 0x1001 0x80>,
-								 <&apps_smmu 0x1061 0x0>;
-						};
-					};
-
-					q6prm: service@2 {
-						compatible = "qcom,q6prm";
-						reg = <GPR_PRM_MODULE_IID>;
-						qcom,protection-domain = "avs/audio",
-									 "msm/adsp/audio_pd";
-
-						q6prmcc: clock-controller {
-							compatible = "qcom,q6prm-lpass-clocks";
-							#clock-cells = <2>;
-						};
-					};
-				};
-			};
-		};
-
 		lpass_wsa2macro: codec@6aa0000 {
 			compatible = "qcom,sm8650-lpass-wsa-macro", "qcom,sm8550-lpass-wsa-macro";
 			reg = <0 0x06aa0000 0 0x1000>;
-			clocks = <&q6prmcc LPASS_CLK_ID_WSA2_CORE_TX_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&lpass_vamacro>;
-			clock-names = "mclk",
-				      "macro",
-				      "dcodec",
-				      "fsgen";
+
+			clocks = <0>, <0>, <0>, <&lpass_vamacro>;
+			clock-names = "mclk", "macro", "dcodec", "fsgen";
 
 			#clock-cells = <0>;
 			clock-output-names = "wsa2-mclk";
 			#sound-dai-cells = <1>;
+			status = "disabled";
 		};
 
 		swr3: soundwire@6ab0000 {
@@ -4711,18 +4171,14 @@ swr3: soundwire@6ab0000 {
 		lpass_rxmacro: codec@6ac0000 {
 			compatible = "qcom,sm8650-lpass-rx-macro", "qcom,sm8550-lpass-rx-macro";
 			reg = <0 0x06ac0000 0 0x1000>;
-			clocks = <&q6prmcc LPASS_CLK_ID_RX_CORE_TX_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&lpass_vamacro>;
-			clock-names = "mclk",
-				      "macro",
-				      "dcodec",
-				      "fsgen";
+
+			clocks = <0>, <0>, <0>, <&lpass_vamacro>;
+			clock-names = "mclk", "macro", "dcodec", "fsgen";
 
 			#clock-cells = <0>;
 			clock-output-names = "mclk";
 			#sound-dai-cells = <1>;
+			status = "disabled";
 		};
 
 		swr1: soundwire@6ad0000 {
@@ -4758,35 +4214,27 @@ swr1: soundwire@6ad0000 {
 		lpass_txmacro: codec@6ae0000 {
 			compatible = "qcom,sm8650-lpass-tx-macro", "qcom,sm8550-lpass-tx-macro";
 			reg = <0 0x06ae0000 0 0x1000>;
-			clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&lpass_vamacro>;
-			clock-names = "mclk",
-				      "macro",
-				      "dcodec",
-				      "fsgen";
+
+			clocks = <0>, <0>, <0>, <&lpass_vamacro>;
+			clock-names = "mclk", "macro", "dcodec", "fsgen";
 
 			#clock-cells = <0>;
 			clock-output-names = "mclk";
 			#sound-dai-cells = <1>;
+			status = "disabled";
 		};
 
 		lpass_wsamacro: codec@6b00000 {
 			compatible = "qcom,sm8650-lpass-wsa-macro", "qcom,sm8550-lpass-wsa-macro";
 			reg = <0 0x06b00000 0 0x1000>;
-			clocks = <&q6prmcc LPASS_CLK_ID_WSA_CORE_TX_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&lpass_vamacro>;
-			clock-names = "mclk",
-				      "macro",
-				      "dcodec",
-				      "fsgen";
+
+			clocks = <0>, <0>, <0>, <&lpass_vamacro>;
+			clock-names = "mclk", "macro", "dcodec", "fsgen";
 
 			#clock-cells = <0>;
 			clock-output-names = "mclk";
 			#sound-dai-cells = <1>;
+			status = "disabled";
 		};
 
 		swr0: soundwire@6b10000 {
@@ -4854,12 +4302,9 @@ swr2: soundwire@6d30000 {
 		lpass_vamacro: codec@6d44000 {
 			compatible = "qcom,sm8650-lpass-va-macro", "qcom,sm8550-lpass-va-macro";
 			reg = <0 0x06d44000 0 0x1000>;
-			clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
-			clock-names = "mclk",
-				      "macro",
-				      "dcodec";
+
+			clocks = <0>, <0>, <0>;
+			clock-names = "mclk", "macro", "dcodec";
 
 			#clock-cells = <0>;
 			clock-output-names = "fsgen";
@@ -4870,14 +4315,15 @@ lpass_tlmm: pinctrl@6e80000 {
 			compatible = "qcom,sm8650-lpass-lpi-pinctrl";
 			reg = <0 0x06e80000 0 0x20000>;
 
-			clocks = <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
-				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+			clocks = <0>, <0>;
 			clock-names = "core", "audio";
 
 			gpio-controller;
 			#gpio-cells = <2>;
 			gpio-ranges = <&lpass_tlmm 0 0 23>;
 
+			status = "disabled";
+
 			tx_swr_active: tx-swr-active-state {
 				clk-pins {
 					pins = "gpio0";
@@ -7662,182 +7108,6 @@ nsp_noc: interconnect@320c0000 {
 
 			#interconnect-cells = <2>;
 		};
-
-		remoteproc_cdsp: remoteproc@32300000 {
-			compatible = "qcom,sm8650-cdsp-pas";
-			reg = <0x0 0x32300000 0x0 0x10000>;
-
-			interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING 0>,
-					      <&smp2p_cdsp_in 0 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_cdsp_in 1 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_cdsp_in 2 IRQ_TYPE_EDGE_RISING>,
-					      <&smp2p_cdsp_in 3 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "wdog",
-					  "fatal",
-					  "ready",
-					  "handover",
-					  "stop-ack";
-
-			clocks = <&rpmhcc RPMH_CXO_CLK>;
-			clock-names = "xo";
-
-			interconnects = <&nsp_noc MASTER_CDSP_PROC QCOM_ICC_TAG_ALWAYS
-					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
-
-			power-domains = <&rpmhpd RPMHPD_CX>,
-					<&rpmhpd RPMHPD_MXC>,
-					<&rpmhpd RPMHPD_NSP>;
-			power-domain-names = "cx",
-					     "mxc",
-					     "nsp";
-
-			memory-region = <&cdsp_mem>, <&q6_cdsp_dtb_mem>, <&global_sync_mem>;
-
-			qcom,qmp = <&aoss_qmp>;
-
-			qcom,smem-states = <&smp2p_cdsp_out 0>;
-			qcom,smem-state-names = "stop";
-
-			status = "disabled";
-
-			glink-edge {
-				interrupts-extended = <&ipcc IPCC_CLIENT_CDSP
-							     IPCC_MPROC_SIGNAL_GLINK_QMP
-							     IRQ_TYPE_EDGE_RISING>;
-
-				mboxes = <&ipcc IPCC_CLIENT_CDSP
-						IPCC_MPROC_SIGNAL_GLINK_QMP>;
-
-				qcom,remote-pid = <5>;
-
-				label = "cdsp";
-
-				fastrpc {
-					compatible = "qcom,fastrpc";
-
-					qcom,glink-channels = "fastrpcglink-apps-dsp";
-
-					label = "cdsp";
-
-					qcom,non-secure-domain;
-
-					#address-cells = <1>;
-					#size-cells = <0>;
-
-					compute-cb@1 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <1>;
-
-						iommus = <&apps_smmu 0x1961 0x0>,
-							 <&apps_smmu 0x0c01 0x20>,
-							 <&apps_smmu 0x19c1 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@2 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <2>;
-
-						iommus = <&apps_smmu 0x1962 0x0>,
-							 <&apps_smmu 0x0c02 0x20>,
-							 <&apps_smmu 0x19c2 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@3 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <3>;
-
-						iommus = <&apps_smmu 0x1963 0x0>,
-							 <&apps_smmu 0x0c03 0x20>,
-							 <&apps_smmu 0x19c3 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@4 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <4>;
-
-						iommus = <&apps_smmu 0x1964 0x0>,
-							 <&apps_smmu 0x0c04 0x20>,
-							 <&apps_smmu 0x19c4 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@5 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <5>;
-
-						iommus = <&apps_smmu 0x1965 0x0>,
-							 <&apps_smmu 0x0c05 0x20>,
-							 <&apps_smmu 0x19c5 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@6 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <6>;
-
-						iommus = <&apps_smmu 0x1966 0x0>,
-							 <&apps_smmu 0x0c06 0x20>,
-							 <&apps_smmu 0x19c6 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@7 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <7>;
-
-						iommus = <&apps_smmu 0x1967 0x0>,
-							 <&apps_smmu 0x0c07 0x20>,
-							 <&apps_smmu 0x19c7 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@8 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <8>;
-
-						iommus = <&apps_smmu 0x1968 0x0>,
-							 <&apps_smmu 0x0c08 0x20>,
-							 <&apps_smmu 0x19c8 0x0>;
-						dma-coherent;
-					};
-
-					/* note: secure cb9 in downstream */
-
-					compute-cb@12 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <12>;
-
-						iommus = <&apps_smmu 0x196c 0x0>,
-							 <&apps_smmu 0x0c0c 0x20>,
-							 <&apps_smmu 0x19cc 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@13 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <13>;
-
-						iommus = <&apps_smmu 0x196d 0x0>,
-							 <&apps_smmu 0x0c0d 0x20>,
-							 <&apps_smmu 0x19cd 0x0>;
-						dma-coherent;
-					};
-
-					compute-cb@14 {
-						compatible = "qcom,fastrpc-compute-cb";
-						reg = <14>;
-
-						iommus = <&apps_smmu 0x196e 0x0>,
-							 <&apps_smmu 0x0c0e 0x20>,
-							 <&apps_smmu 0x19ce 0x0>;
-						dma-coherent;
-					};
-				};
-			};
-		};
 	};
 
 	thermal-zones {

-- 
2.34.1


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

* [PATCH RFC 21/23] arm64: dts: qcom: add SM8650 LPASS CPU dtsi
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (19 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:18   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 22/23] arm64: dts: qcom: sm8650-valve-deckard: enable audio Neil Armstrong
  2026-11-13 18:38 ` [PATCH RFC 23/23] arm64: defconfig: enable the SM8650 LPASS CPU driver Neil Armstrong
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Boards booting at EL2 without the ADSP audio firmware need the LPASS clock
controllers and lpass-cpu nodes to drive audio directly.

Add sm8650-lpass-cpu.dtsi with these nodes and the LPASS macros clocks,
and the I2S2 pinctrl state.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi | 222 +++++++++++++++++++++++++
 arch/arm64/boot/dts/qcom/sm8650.dtsi           |  25 +++
 2 files changed, 247 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi b/arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi
new file mode 100644
index 000000000000..43d0d8daa4fe
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2026, Linaro Limited
+ */
+
+#include <dt-bindings/clock/qcom,lpasscorecc-sm8650.h>
+#include <dt-bindings/clock/qcom,lpassaudiocc-sm8650.h>
+#include <dt-bindings/sound/qcom,lpass.h>
+
+&lpass_rxmacro {
+	clocks = <&lpass_audiocc LPASS_AUDIO_CC_RX_MCLK_CLK>,
+		 <&lpass_audiocc LPASS_AUDIO_CC_TX_MCLK_RX_CLK>,
+		 <&lpass_vamacro>;
+	clock-names = "mclk",
+		      "macro",
+		      "fsgen";
+
+	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
+			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+	power-domain-names = "macro",
+			     "dcodec";
+};
+
+&lpass_tlmm {
+	clocks = <&lpass_core LPASS_CORE_CC_CORE_CLK>;
+	clock-names = "core";
+
+	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
+			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+	power-domain-names = "core",
+			     "audio";
+
+	status = "okay";
+};
+
+&lpass_txmacro {
+	clocks = <&lpass_aon LPASS_AON_CC_TX_MCLK_CLK>,
+		 <&lpass_vamacro>;
+	clock-names = "mclk",
+		      "fsgen";
+
+	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
+			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+	power-domain-names = "macro",
+			     "dcodec";
+};
+
+&lpass_vamacro {
+	clocks = <&lpass_aon LPASS_AON_CC_TX_MCLK_CLK>;
+	clock-names = "mclk";
+
+	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
+			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+	power-domain-names = "macro",
+			     "dcodec";
+};
+
+&lpass_wsa2macro {
+	clocks = <&lpass_audiocc LPASS_AUDIO_CC_WSA2_MCLK_CLK>,
+		 <&lpass_audiocc LPASS_AUDIO_CC_TX_MCLK_WSA2_CLK>,
+		 <&lpass_vamacro>;
+	clock-names = "mclk",
+		      "macro",
+		      "fsgen";
+
+	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
+			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+	power-domain-names = "macro",
+			     "dcodec";
+};
+
+&lpass_wsamacro {
+	clocks = <&lpass_audiocc LPASS_AUDIO_CC_WSA_MCLK_CLK>,
+		 <&lpass_audiocc LPASS_AUDIO_CC_TX_MCLK_WSA_CLK>,
+		 <&lpass_vamacro>;
+	clock-names = "mclk",
+		      "macro",
+		      "fsgen";
+
+	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
+			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+	power-domain-names = "macro",
+			     "dcodec";
+};
+
+&soc {
+	lpass_audiocc: clock-controller@6bc0000 {
+		reg = <0 0x06bc0000 0 0x38000>;
+		compatible = "qcom,sm8650-lpassaudiocc";
+
+		clocks = <&bi_tcxo_div2>,
+			 <&lpass_aon LPASS_AON_CC_MAIN_RCG_CLK_SRC>;
+		clock-names = "bi_tcxo",
+			      "iface";
+
+		power-domains = <&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+
+		#clock-cells = <1>;
+	};
+
+	lpass_aon: clock-controller@6e00000 {
+		reg = <0 0x06e00000 0 0x30000>;
+		compatible = "qcom,sm8650-lpassaoncc";
+
+		clocks = <&bi_tcxo_div2>,
+			 <&lpass_core LPASS_CORE_CC_CORE_CLK>;
+		clock-names = "bi_tcxo",
+			      "iface";
+
+		#clock-cells = <1>;
+		#power-domain-cells = <1>;
+	};
+
+	lpass_core: clock-controller@7b00000 {
+		compatible = "qcom,sm8650-lpasscorecc";
+		reg = <0 0x07b00000 0 0x58000>;
+
+		clocks = <&bi_tcxo_div2>;
+		clock-names = "bi_tcxo";
+
+		power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>;
+
+		#clock-cells = <1>;
+	};
+
+	lpass_hm: clock-controller@7e00000 {
+		compatible = "qcom,sm8650-lpasshm";
+		reg = <0 0x07e00000 0 0xa000>;
+
+		clocks = <&bi_tcxo_div2>;
+		clock-names = "bi_tcxo";
+
+		power-domains = <&rpmhpd RPMHPD_LCX>;
+
+		required-opps = <&rpmhpd_opp_nom>;
+
+		#clock-cells = <1>;
+		#power-domain-cells = <1>;
+	};
+
+	lpass_cpu: audio@7b87000 {
+		compatible = "qcom,sm8650-lpass-cpu";
+
+		reg = <0 0x07b87000 0 0x68000>,
+		      <0 0x07d00000 0 0x2b000>,
+		      <0 0x06c80000 0 0x10000>,
+		      <0 0x06b40000 0 0x2b000>,
+		      <0 0x06d00000 0 0x2b000>,
+		      <0 0x06d40000 0 0x3000>,
+		      <0 0x06b80000 0 0x2b000>,
+		      <0 0x06a40000 0 0x2b000>;
+		reg-names = "lpass-hdmiif",
+			    "lpass-lpaif",
+			    "lpass-rxtx-cdc-dma-lpm",
+			    "lpass-rxtx-lpaif",
+			    "lpass-va-lpaif",
+			    "lpass-va-cdc-dma-lpm",
+			    "lpass-wsa-lpaif",
+			    "lpass-aud-lpaif";
+
+		interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>,
+			     <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH 0>;
+		interrupt-names = "lpass-irq-lpaif",
+				  "lpass-irq-hdmi",
+				  "lpass-irq-vaif",
+				  "lpass-irq-rxtxif",
+				  "lpass-irq-wsaif",
+				  "lpass-irq-audif";
+
+		clocks = <&lpass_aon LPASS_AON_CC_AUDIO_HM_H_CLK>,
+			 <&lpass_core LPASS_CORE_CC_SYSNOC_MPORT_CORE_CLK>,
+			 <&lpass_core LPASS_CORE_CC_EXT_MCLK0_CLK>,
+			 <&lpass_core LPASS_CORE_CC_EXT_IF0_IBIT_CLK>,
+			 <&lpass_core LPASS_CORE_CC_EXT_IF1_IBIT_CLK>,
+			 <&lpass_aon LPASS_AON_CC_EXT_IF1_IBIT_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_EXT_IF1_IBIT_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_EXT_IF2_IBIT_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_EXT_IF4_IBIT_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_EXT_IF5_IBIT_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_CODEC_MEM_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_CODEC_MEM0_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_CODEC_MEM1_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_CODEC_MEM2_CLK>,
+			 <&lpass_audiocc LPASS_AUDIO_CC_CODEC_MEM3_CLK>,
+			 <&lpass_aon LPASS_AON_CC_VA_MEM0_CLK>;
+		clock-names = "aon_cc_audio_hm_h",
+			      "core_cc_sysnoc_mport_core",
+			      "core_cc_ext_mclk0",
+			      "core_cc_ext_if0_ibit",
+			      "core_cc_ext_if1_ibit",
+			      "aon_cc_ext_if1_ibit",
+			      "audio_cc_ext_if1_ibit",
+			      "audio_cc_ext_if2_ibit",
+			      "audio_cc_ext_if4_ibit",
+			      "audio_cc_ext_if5_ibit",
+			      "audio_cc_codec_mem",
+			      "audio_cc_codec_mem0",
+			      "audio_cc_codec_mem1",
+			      "audio_cc_codec_mem2",
+			      "audio_cc_codec_mem3",
+			      "aon_cc_va_mem0";
+
+		iommus = <&apps_smmu 0x1100 0>,
+			 <&apps_smmu 0x1101 0>,
+			 <&apps_smmu 0x1102 0>;
+
+		power-domains = <&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
+
+		dma-coherent;
+
+		#sound-dai-cells = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		status = "disabled";
+	};
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi
index d88acdb8c775..298e4ca33b4b 100644
--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi
@@ -4427,6 +4427,31 @@ data-pins {
 					bias-bus-hold;
 				};
 			};
+
+			i2s2_active: i2s2-active-state {
+				clk-pins {
+					pins = "gpio10";
+					function = "i2s2_clk";
+					drive-strength = <8>;
+					bias-disable;
+					output-high;
+				};
+
+				ws-pins {
+					pins = "gpio11";
+					function = "i2s2_ws";
+					drive-strength = <8>;
+					output-high;
+				};
+
+				data-pins {
+					pins = "gpio15", "gpio16";
+					function = "i2s2_data";
+					drive-strength = <8>;
+					bias-disable;
+					output-high;
+				};
+			};
 		};
 
 		lpass_lpiaon_noc: interconnect@7400000 {

-- 
2.34.1


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

* [PATCH RFC 22/23] arm64: dts: qcom: sm8650-valve-deckard: enable audio
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (20 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 21/23] arm64: dts: qcom: add SM8650 LPASS CPU dtsi Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  2026-11-13 19:23   ` sashiko-bot
  2026-11-13 18:38 ` [PATCH RFC 23/23] arm64: defconfig: enable the SM8650 LPASS CPU driver Neil Armstrong
  22 siblings, 1 reply; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Deckard boots Linux at EL2 without the ADSP audio firmware, its speakers
and DMICs need to be driven through lpass-cpu.

Include sm8650-lpass-cpu.dtsi and add the SM8650 sound card with the
speakers amplifiers on Senary MI2S and the DMICs on the VA macro.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts | 64 +++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts b/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
index a365caa28cee..723f7911fc54 100644
--- a/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
+++ b/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
@@ -16,6 +16,7 @@
 #include "pm8550ve.dtsi"
 #include "pm8550vs.dtsi"
 #include "pmk8550.dtsi"
+#include "sm8650-lpass-cpu.dtsi"
 
 / {
 	model = "Valve Deckard";
@@ -160,6 +161,37 @@ framebuffer_mem: framebuffer-region@af400000 {
 		};
 	};
 
+	sound: sound {
+		compatible = "qcom,sm8650-sndcard", "qcom,sm8450-sndcard";
+		model = "VALVE-DECKARD";
+
+		audio-routing = "VA DMIC0", "vdd-micb";
+
+		speaker-dai-link {
+			link-name = "SPKR Playback";
+
+			cpu {
+				sound-dai = <&lpass_cpu MI2S_SENARY>;
+			};
+
+			codec {
+				sound-dai = <&left_spkr_i2s>, <&right_spkr_i2s>;
+			};
+		};
+
+		va-dai-link {
+			link-name = "VA Capture";
+
+			cpu {
+				sound-dai = <&lpass_cpu LPASS_CDC_DMA_VA_TX0>;
+			};
+
+			codec {
+				sound-dai = <&lpass_vamacro 0>;
+			};
+		};
+	};
+
 	wcn7850-pmu {
 		compatible = "qcom,wcn7850-pmu";
 
@@ -776,6 +808,38 @@ eeprom@50 {
 	};
 };
 
+&lpass_cpu {
+	status = "okay";
+
+	pinctrl-0 = <&i2s2_active>;
+	pinctrl-names = "default";
+
+	dai-link@22 {
+		reg = <MI2S_SENARY>;
+		qcom,playback-sd-lines = <0>;
+	};
+
+	dai-link@19 {
+		reg = <LPASS_CDC_DMA_VA_TX0>;
+	};
+
+};
+
+&lpass_rxmacro {
+	status = "okay";
+};
+
+&lpass_txmacro {
+	status = "okay";
+};
+
+&lpass_vamacro {
+	pinctrl-0 = <&dmic01_default>;
+	pinctrl-names = "default";
+	vdd-micb-supply = <&vreg_l10b_1p8>;
+	qcom,dmic-sample-rate = <2400000>;
+};
+
 &pcie0 {
 	wake-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>;
 	perst-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>;

-- 
2.34.1


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

* [PATCH RFC 23/23] arm64: defconfig: enable the SM8650 LPASS CPU driver
  2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
                   ` (21 preceding siblings ...)
  2026-11-13 18:38 ` [PATCH RFC 22/23] arm64: dts: qcom: sm8650-valve-deckard: enable audio Neil Armstrong
@ 2026-11-13 18:38 ` Neil Armstrong
  22 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2026-11-13 18:38 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Abel Vesa, Stephen Boyd,
	Brian Masney, Jerome Brunet, Srinivas Kandagatla, Rohit kumar,
	Jaroslav Kysela, Takashi Iwai, Konrad Dybcio, Bartosz Golaszewski,
	Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio, Neil Armstrong

Boards like the Valve Deckard drive the SM8650 audio interfaces from Linux.

Enable the SM8650 LPASS CPU driver as a module.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 7b4985b29b21..a2dc062f5b33 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1116,6 +1116,7 @@ CONFIG_SND_SOC_MT8365_MT6357=m
 CONFIG_SND_MESON_AXG_SOUND_CARD=m
 CONFIG_SND_MESON_GX_SOUND_CARD=m
 CONFIG_SND_SOC_QCOM=m
+CONFIG_SND_SOC_LPASS_SM8650=m
 CONFIG_SND_SOC_APQ8016_SBC=m
 CONFIG_SND_SOC_QDSP6_USB=m
 CONFIG_SND_SOC_MSM8996=m

-- 
2.34.1


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

* Re: [PATCH RFC 07/23] ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS
  2026-11-13 18:38 ` [PATCH RFC 07/23] ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS Neil Armstrong
@ 2026-11-13 18:57   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 18:57 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] ASoC: dt-bindings: qcom,lpass-cpu: split unwieldy per-compatible
  schema
--

commit f17c7c8f20845c2d15f821f60a13b5f93656ae8d
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS

This commit adds the qcom,sm8650-lpass-cpu compatible string and its
register, clock, and interrupt definitions to the qcom,lpass-cpu binding
schema.

> diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
> index 3a559bd07a79b..f431173a0e880 100644
> --- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
> +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
> @@ -23,32 +23,33 @@ properties:
>        - qcom,apq8016-lpass-cpu
>        - qcom,sc7180-lpass-cpu
>        - qcom,sc7280-lpass-cpu
> +      - qcom,sm8650-lpass-cpu
>  
>    reg:
>      minItems: 1
> -    maxItems: 6
> +    maxItems: 8
>      description: LPAIF core registers
>  
>    reg-names:
>      minItems: 1
> -    maxItems: 6
> +    maxItems: 8
>  
>    clocks:
>      minItems: 3
> -    maxItems: 10
> +    maxItems: 16
>  
>    clock-names:
>      minItems: 1
> -    maxItems: 10
> +    maxItems: 16
>  
>    interrupts:
>      minItems: 1
> -    maxItems: 4
> +    maxItems: 6
>      description: LPAIF DMA buffer interrupt
>  
>    interrupt-names:
>      minItems: 1
> -    maxItems: 4
> +    maxItems: 6
>  
>    qcom,adsp:
>      $ref: /schemas/types.yaml#/definitions/phandle
> @@ -59,6 +60,8 @@ properties:
>      maxItems: 3
>      description: Phandle to apps_smmu node with sid mask
>  
> +  dma-coherent: true
> +
>    power-domains:
>      maxItems: 1
>  
> @@ -241,6 +244,65 @@ allOf:
>          - iommus
>          - power-domains
>  
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: qcom,sm8650-lpass-cpu
> +
> +    then:
> +      properties:
> +        clocks:
> +          minItems: 16
> +          maxItems: 16

[Severity: Low]
Should this binding be split into separate schema files (sharing common DAI
definitions via a common schema ref if needed) instead of adding another
large per-compatible if-then block to qcom,lpass-cpu.yaml?

Each SoC variant here has substantially different reg, clock, and interrupt
counts and names.  Fitting qcom,sm8650-lpass-cpu into the same schema also
widens the top-level maxItems limits and enables dma-coherent at the top
level, which relaxes validation for older compatibles like qcom,lpass-cpu
and qcom,apq8016-lpass-cpu whose if-then blocks only constrain clocks and
clock-names.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=7

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

* Re: [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers
  2026-11-13 18:38 ` [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers Neil Armstrong
@ 2026-11-13 18:57   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 18:57 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] dt-bindings: clock: qcom: broken examples in
  qcom,sm8650-lpasscorecc.yaml
- [Low] dt-bindings: clock: qcom: unwieldy per-compatible conditionals
  in schema
--

commit 822c52dcc683f4949600245b7d9f62d13802e752
Author: Neil Armstrong <neil.armstrong@linaro.org>

dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers

This commit adds the Device Tree binding schema and header definitions for
the SM8650 LPASS HM, core, audio, and AON clock controllers.

> diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8650-lpasscorecc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8650-lpasscorecc.yaml
> new file mode 100644
> index 0000000000000..9c5207ee02f15
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/qcom,sm8650-lpasscorecc.yaml

[ ... ]

> +allOf:
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: qcom,sm8650-lpasshm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: Board XO source
> +        clock-names:
> +          items:
> +            - const: bi_tcxo
> +      required:
> +        - power-domains
> +        - '#power-domain-cells'
> +
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: qcom,sm8650-lpasscorecc
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: Board XO source
> +        clock-names:
> +          items:
> +            - const: bi_tcxo
> +        required-opps: false
> +      required:
> +        - power-domains
> +
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: qcom,sm8650-lpassaoncc
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: Board XO source
> +            - description: LPASS_CORE_CC_CORE_CLK
> +        clock-names:
> +          items:
> +            - const: bi_tcxo
> +            - const: iface
> +        power-domains: false
> +        required-opps: false
> +      required:
> +        - '#power-domain-cells'
> +
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: qcom,sm8650-lpassaudiocc
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: Board XO source
> +            - description: LPASS_AON_CC_MAIN_RCG_CLK_SRC
> +        clock-names:
> +          items:
> +            - const: bi_tcxo
> +            - const: iface
> +        required-opps: false
> +      required:
> +        - power-domains

[Severity: Low]
Each of the four compatibles requires its own if-then block to override
clocks, clock-names, power-domains, required-opps, and
#power-domain-cells (which also leaves #power-domain-cells unconstrained
on qcom,sm8650-lpasscorecc and qcom,sm8650-lpassaudiocc).  Would it be
cleaner to split these variants into separate binding schema files instead
of using per-compatible conditionals for every entry?

> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/qcom,lpassaudiocc-sm8650.h>
> +    #include <dt-bindings/clock/qcom,lpasscorecc-sm8650.h>
> +    #include <dt-bindings/clock/qcom,sm8650-gcc.h>
> +    #include <dt-bindings/power/qcom,rpmhpd.h>
> +
> +    clock-controller@6bc0000 {
> +        compatible = "qcom,sm8650-lpassaudiocc";
> +        reg = <0x06bc0000 0x38000>;
> +        clocks = <&bi_tcxo_div2>;
> +        clock-names = "bi_tcxo";
> +        power-domains = <&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
> +        #clock-cells = <1>;
> +    };
> +
> +    lpass_aon: clock-controller@6e00000 {
> +        compatible = "qcom,sm8650-lpassaoncc";
> +        reg = <0x06e00000 0x30000>;
> +        clocks = <&bi_tcxo_div2>, <&lpass_core LPASS_CORE_CC_CORE_CLK>;
> +        clock-names = "bi_tcxo", "iface";
> +        #clock-cells = <1>;
> +        #power-domain-cells = <1>;
> +    };
> +
> +    lpass_core: clock-controller@7b00000 {
> +        compatible = "qcom,sm8650-lpasscorecc";
> +        reg = <0x07b00000 0x58000>;
> +        clocks = <&bi_tcxo_div2>;
> +        clock-names = "bi_tcxo";
> +        power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>;
> +        #clock-cells = <1>;
> +    };
> +
> +    lpass_hm: clock-controller@7e00000 {
> +        compatible = "qcom,sm8650-lpasshm";
> +        reg = <0x07e00000 0xa000>;
> +        clocks = <&bi_tcxo_div2>, <&gcc GCC_LPASS_CFG_NOC_SWAY_CLK>;
> +        clock-names = "bi_tcxo", "iface";
> +        power-domains = <&rpmhpd RPMHPD_LCX>;
> +        required-opps = <&rpmhpd_opp_nom>;
> +        #clock-cells = <1>;
> +        #power-domain-cells = <1>;
> +    };

[Severity: Medium]
Does this example pass make dt_binding_check?

In lpass_hm, GCC_LPASS_CFG_NOC_SWAY_CLK is not defined in
include/dt-bindings/clock/qcom,sm8650-gcc.h, causing dtc to fail with a
syntax error.  In addition, lpass_hm specifies two clocks ("bi_tcxo" and
"iface") while the qcom,sm8650-lpasshm conditional schema only allows
"bi_tcxo", and clock-controller@6bc0000 (qcom,sm8650-lpassaudiocc) only
provides "bi_tcxo" while its conditional schema requires both "bi_tcxo"
and "iface".

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=1

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

* Re: [PATCH RFC 03/23] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used
  2026-11-13 18:38 ` [PATCH RFC 03/23] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used Neil Armstrong
@ 2026-11-13 19:02   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:02 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: redundant
  oneOf in clocks
--

commit d8422bb265d6e088cce5d6024805eb9ced00d38a
Author: Neil Armstrong <neil.armstrong@linaro.org>

dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used

This commit updates the SM8650 LPASS LPI pinctrl DT binding schema to
allow a single core clock and add power-domains properties for platforms
where the ADSP is bypassed.

> diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
> index 1bf08860a4baf..4c54f873199cc 100644
> --- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
> +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
> @@ -29,11 +29,25 @@ properties:
>        - description: LPASS LPI TLMM Control and Status registers
>  
>    clocks:
> -    items:
> -      - description: LPASS Core voting clock
> -      - description: LPASS Audio voting clock
> +    oneOf:
> +      - items:   # for ADSP based platforms
> +          - description: LPASS Core voting clock
> +          - description: LPASS Audio voting clock
> +      - items:   # for ADSP bypass based platforms
> +          - description: LPASS Core voting clock
>  
>    clock-names:
> +    oneOf:
> +      - items:   # for ADSP based platforms
> +          - const: core
> +          - const: audio
> +      - items:   # for ADSP bypass based platforms
> +          - const: core

[Severity: Low]
Could clocks and clock-names set minItems: 1 alongside the two-entry
items list instead of using oneOf?  Because the single-entry ADSP bypass
list is a strict prefix of the two-entry list, minItems: 1 avoids
duplicating the first item in both properties.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=3

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

* Re: [PATCH RFC 13/23] ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs
  2026-11-13 18:38 ` [PATCH RFC 13/23] ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs Neil Armstrong
@ 2026-11-13 19:04   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:04 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] ASoC: qcom: lpass: NULL deref in lpass_cpu_daiops_probe() on
  unprobed LPAIF
--

commit 4c412765d7dfc4828c0e1ec1c2ada16e697ee595
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs

This commit adds an optional lpaif_mi2s_desc descriptor to struct
lpass_variant so MI2S ports can be mapped to and probed across multiple
LPAIF instances.

> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index f8b9b2ec90553..411eecd9cbb93 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
> @@ -1079,6 +1079,8 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
>  			data->mi2s_capture_sd_mode[port_id] =
>  				of_lpass_cpu_parse_sd_lines(dev, node,
>  						    "qcom,capture-sd-lines");
> +
> +			set_bit(port_id, &data->lpaif_port_enable);
>  		} else {
>  			dev_err(dev, "invalid dai id: %d\n", id);
>  			continue;
> @@ -1121,12 +1123,33 @@ static int lpass_cpu_init_lpaif(struct platform_device *pdev, int idx,
>  	struct regmap_config cfg = lpass_cpu_regmap_config;
>  	struct device *dev = &pdev->dev;
>  	void __iomem *base;
> -	int ret;
> +	int ret, i;
> +
> +	/* Only probe an LPAIF if one of the port is described in DT */
> +	if (v->mi2s_desc) {
> +		const struct lpaif_mi2s_desc *desc = &v->mi2s_desc[idx];
> +		bool enabled = false;
> +
> +		for (i = 0; i < desc->port_desc_count; ++i) {
> +			unsigned int port_id = mi2s_port_id(desc->port_desc[i].id);
> +
> +			if (test_bit(port_id, &drvdata->lpaif_port_enable)) {
> +				enabled = true;
> +				break;
> +			}
> +		}
> +
> +		if (!enabled)
> +			return 0;
> +	}

[ ... ]

> @@ -1283,8 +1320,30 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>  
>  		port_id = mi2s_port_id(variant->dai_driver[i].id);
>  
> -		drvdata->mi2s_lpaif[port_id] = &drvdata->lpaif[0];
> -		drvdata->mi2s_intf[port_id] = port_id;
> +		if (variant->mi2s_desc) {
> +			unsigned int lpaif_idx, lpaif_port;
> +			const struct lpaif_mi2s_desc *desc;
> +
> +			for (lpaif_idx = 0; lpaif_idx < variant->num_mi2s_desc; ++lpaif_idx) {
> +				desc = &variant->mi2s_desc[lpaif_idx];
> +
> +				for (lpaif_port = 0;
> +				     lpaif_port < desc->port_desc_count;
> +				     ++lpaif_port) {
> +					if (desc->port_desc[lpaif_port].id !=
> +							variant->dai_driver[i].id)
> +						continue;
> +
> +					drvdata->mi2s_lpaif[port_id] =
> +						&drvdata->lpaif[lpaif_idx];
> +					drvdata->mi2s_intf[port_id] =
> +						desc->port_desc[lpaif_port].intf;
> +				}
> +			}
> +		} else {
> +			drvdata->mi2s_lpaif[port_id] = &drvdata->lpaif[0];
> +			drvdata->mi2s_intf[port_id] = port_id;
> +		}

[Severity: Medium]
Can this cause a NULL pointer dereference in lpass_cpu_daiops_probe() when
a sound card links to an MI2S DAI whose LPAIF was skipped by
lpass_cpu_init_lpaif()?

of_lpass_cpu_parse_dai_data() only sets lpaif_port_enable for MI2S ports
that have an explicit child node under the lpass-cpu DT node, while
leaving child nodes optional when using the default 8-channel SD line
configuration.  When lpass_cpu_init_lpaif() returns 0 early because
lpaif_port_enable is not set for any port on that LPAIF, lpaif->drvdata,
lpaif->map and lpaif->i2sctl remain NULL, and lpass_platform_init_lpaif()
likewise skips initializing lpaif->rd_dmactl and lpaif->wr_dmactl.

However, asoc_qcom_lpass_cpu_platform_probe() still points
drvdata->mi2s_lpaif[port_id] at &drvdata->lpaif[lpaif_idx] (or leaves it
NULL if the DAI is not listed in variant->mi2s_desc) and registers all
variant->dai_driver DAIs.  Unlike the platform suspend/resume paths,
lpass_cpu_daiops_probe() and lpass_platform_pcmops_open() do not check
whether the LPAIF was initialized before dereferencing lpaif and passing
lpaif->map to regmap_write():

sound/soc/qcom/lpass-cpu.c:lpass_cpu_daiops_probe() {
	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
	unsigned int port_id = mi2s_port_id(dai->driver->id);
	struct lpass_lpaif *lpaif = drvdata->mi2s_lpaif[port_id];
	int ret;

	/* ensure audio hardware is disabled */
	ret = regmap_write(lpaif->map,
			LPAIF_I2SCTL_REG(drvdata->variant, drvdata->mi2s_intf[port_id]), 0);
    ...
}

Checking !lpaif || !lpaif->drvdata in lpass_cpu_daiops_probe() and
returning -ENODEV would prevent probing an uninitialized LPAIF.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=13

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

* Re: [PATCH RFC 16/23] ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory
  2026-11-13 18:38 ` [PATCH RFC 16/23] ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory Neil Armstrong
@ 2026-11-13 19:06   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:06 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] ASoC: qcom: lpass: LPM clocks not parsed when only
  mi2s_lpm_enable is set
- [Medium] ASoC: qcom: lpass: unchecked mem_clk enable and leak in
  startup()
--

commit 6968d3f5a8288f5418d4e48725abed1263de22d5
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory

This commit adds a helper to select the codec_mem clock matching a given LPM
buffer offset and enables the corresponding LPM memory clock during DAI
startup and shutdown for Codec DMA and LPM-backed MI2S ports.

> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index 4bbcd59ebd602..302c31b23090f 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
> @@ -89,6 +89,17 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
>  	unsigned int port_id = mi2s_port_id(dai->driver->id);
>  	int ret;
>  
> +	if (is_lpm_mi2s_port(drvdata, dai->driver->id)) {
> +		struct clk *mem_clk = drvdata->va_mem0;
> +
> +		if (drvdata->mi2s_lpm[port_id] == LPASS_RXTX_CDC_LPM)
> +			mem_clk = lpass_cdc_dma_mem_clk(drvdata,
> +							drvdata->mi2s_lpm_offset[port_id]);

[Severity: Medium]
Can mem_clk be NULL here when a board enables LPM-backed MI2S ports without
any Codec DMA DAIs?

In asoc_qcom_lpass_cpu_platform_probe(), of_lpass_cdc_dma_clks_parse() is only
called when drvdata->codec_dma_enable is set, whereas the LPM memory resources
are fetched when either codec_dma_enable or mi2s_lpm_enable is set:

sound/soc/qcom/lpass-cpu.c:asoc_qcom_lpass_cpu_platform_probe() {
    ...
		ret = of_lpass_cdc_dma_clks_parse(dev, drvdata);
		if (ret) {
			dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
			return ret;
		}
	}
    ...
	if (drvdata->codec_dma_enable || drvdata->mi2s_lpm_enable) {
		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
    ...
}

When only drvdata->mi2s_lpm_enable is set, drvdata->va_mem0 and
drvdata->codec_mem0..3 are never populated and remain NULL.  Passing NULL to
clk_set_rate() and clk_prepare_enable() is a no-op, leaving the LPM SRAM
unclocked when the stream accesses the LPM buffer.  Calling
of_lpass_cdc_dma_clks_parse() under the codec_dma_enable || mi2s_lpm_enable
check would ensure the clocks are acquired.

> +
> +		clk_set_rate(mem_clk, LPASS_CODEC_MEM_HZ_NORMAL);
> +		clk_prepare_enable(mem_clk);
> +	}
> +
>  	ret = clk_prepare_enable(drvdata->mi2s_osr_clk[port_id]);
>  	if (ret) {
>  		dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);

[Severity: Medium]
Does this leak the prepare and enable references on mem_clk if enabling
mi2s_osr_clk or preparing mi2s_bit_clk fails?

Whendai->driver->ops->startup() returns an error, snd_soc_dai_startup() does
not mark the DAI as active, so snd_soc_dai_shutdown() will not call
lpass_cpu_daiops_shutdown() to release mem_clk.  In addition, neither
lpass_cpu_daiops_startup() nor lpass_cdc_dma_daiops_startup() checks the
return values of clk_set_rate() and clk_prepare_enable() on mem_clk, which can
leave LPM memory unclocked and cause an unbalanced clk_disable_unprepare() on
shutdown.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=16

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

* Re: [PATCH RFC 10/23] ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA
  2026-11-13 18:38 ` [PATCH RFC 10/23] ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA Neil Armstrong
@ 2026-11-13 19:07   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:07 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] ASoC: qcom: missing WSA_TX4 case in
  __lpass_get_codec_dma_intf_type()
- [Medium] ASoC: qcom: unchecked clk_prepare_enable() in
  lpass_cdc_dma_daiops_startup()
--

commit fe9e37da583fba7a4cfa98118783d3ccffae7524
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA

This commit adds WSA codec DMA interface plumbing and clock handling to
the LPASS codec DMA driver.

> diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
> index 2dc8c75c4bf08..903055e3188d8 100644
> --- a/sound/soc/qcom/lpass-cdc-dma.c
> +++ b/sound/soc/qcom/lpass-cdc-dma.c

[ ... ]

> @@ -68,49 +76,67 @@ static int __lpass_get_codec_dma_intf_type(int dai_id)
>  	case LPASS_CDC_DMA_RX0:
>  	case LPASS_CDC_DMA_TX0:
>  	case LPASS_CDC_DMA_VA_TX0:
> +	case LPASS_CDC_DMA_WSA_TX0:
> +	case LPASS_CDC_DMA_WSA_RX0:
>  		ret = LPASS_CDC_DMA_INTERFACE1;
>  		break;
>  	case LPASS_CDC_DMA_RX1:
>  	case LPASS_CDC_DMA_TX1:
>  	case LPASS_CDC_DMA_VA_TX1:
> +	case LPASS_CDC_DMA_WSA_RX1:
> +	case LPASS_CDC_DMA_WSA_TX1:
>  		ret = LPASS_CDC_DMA_INTERFACE2;
>  		break;
>  	case LPASS_CDC_DMA_RX2:
>  	case LPASS_CDC_DMA_TX2:
>  	case LPASS_CDC_DMA_VA_TX2:
> +	case LPASS_CDC_DMA_WSA_RX2:
> +	case LPASS_CDC_DMA_WSA_TX2:
>  		ret = LPASS_CDC_DMA_INTERFACE3;
>  		break;
>  	case LPASS_CDC_DMA_RX3:
>  	case LPASS_CDC_DMA_TX3:
>  	case LPASS_CDC_DMA_VA_TX3:
> +	case LPASS_CDC_DMA_WSA_RX3:
> +	case LPASS_CDC_DMA_WSA_TX3:
>  		ret = LPASS_CDC_DMA_INTERFACE4;
>  		break;
>  	case LPASS_CDC_DMA_RX4:
>  	case LPASS_CDC_DMA_TX4:
>  	case LPASS_CDC_DMA_VA_TX4:
> +	case LPASS_CDC_DMA_WSA_RX4:
>  		ret = LPASS_CDC_DMA_INTERFACE5;
>  		break;

[Severity: Medium]
Should LPASS_CDC_DMA_WSA_TX4 also be included in the
LPASS_CDC_DMA_INTERFACE5 case?

__lpass_get_dmactl_handle(), lpass_cdc_dma_daiops_startup(), and
lpass_cdc_dma_daiops_shutdown() all handle the full
LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8 range, but omitting
LPASS_CDC_DMA_WSA_TX4 here causes __lpass_get_codec_dma_intf_type() to
return -EINVAL and __lpass_platform_codec_intf_init() to abort without
configuring or enabling the DMA interface.

[ ... ]

> @@ -184,6 +210,17 @@ static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
>  		clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL);
>  		clk_prepare_enable(drvdata->va_mem0);
>  		break;
> +	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
> +	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
> +		clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);	//??
> +		clk_set_rate(drvdata->codec_mem1, CODEC_MEM_HZ_NORMAL);	//??
> +		clk_set_rate(drvdata->codec_mem2, CODEC_MEM_HZ_NORMAL);	//??
> +		clk_set_rate(drvdata->codec_mem3, CODEC_MEM_HZ_NORMAL);	//??
> +		clk_prepare_enable(drvdata->codec_mem0);		//??
> +		clk_prepare_enable(drvdata->codec_mem1);		//??
> +		clk_prepare_enable(drvdata->codec_mem2);		//??
> +		clk_prepare_enable(drvdata->codec_mem3);		//??
> +		break;
>  	default:
>  		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
>  		break;
> @@ -205,6 +242,13 @@ static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream,
>  	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
>  		clk_disable_unprepare(drvdata->va_mem0);
>  		break;
> +	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
> +	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
> +		clk_disable_unprepare(drvdata->codec_mem3);
> +		clk_disable_unprepare(drvdata->codec_mem2);
> +		clk_disable_unprepare(drvdata->codec_mem1);
> +		clk_disable_unprepare(drvdata->codec_mem0);
> +		break;

[Severity: Medium]
What happens if clk_set_rate() or clk_prepare_enable() fails in
lpass_cdc_dma_daiops_startup()?

Ignoring errors here lets the stream proceed with unclocked LPM memory,
leaves any earlier codec_mem clocks enabled on partial failure, and
causes lpass_cdc_dma_daiops_shutdown() to call clk_disable_unprepare()
on clocks whose enable failed.  Later commit "ASoC: qcom: lpass: enable
the right codec_mem clock when usin LPM memory" removes the "//??"
comments and selects a single clock via lpass_cdc_dma_mem_clk(), but
still leaves clk_set_rate() and clk_prepare_enable() unchecked.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=10

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

* Re: [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi
  2026-11-13 18:38 ` [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi Neil Armstrong
@ 2026-11-13 19:08   ` Val Packett
  2026-11-13 19:11   ` sashiko-bot
  1 sibling, 0 replies; 39+ messages in thread
From: Val Packett @ 2026-11-13 19:08 UTC (permalink / raw)
  To: Neil Armstrong, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Abel Vesa,
	Stephen Boyd, Brian Masney, Jerome Brunet, Srinivas Kandagatla,
	Rohit kumar, Jaroslav Kysela, Takashi Iwai, Konrad Dybcio,
	Bartosz Golaszewski, Linus Walleij
  Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-clk,
	Krzysztof Kozlowski, linux-gpio


On 11/13/26 3:38 PM, Neil Armstrong wrote:
> The reserved memory map, remoteprocs, SMP2P, IPA, GPU zap shader and the
> q6prm LPASS clocks only apply when booting at EL1 under Gunyah, and
> boards booting Linux at EL2 had to delete them.

This might be true in practice for this specific SoC just due to these 
being the only devices made,

but in general, "boots Linux at EL2" does not automatically imply "does 
not use smp2p/remoteprocs" (see Glymur; and earlier laptops with hacks 
that happily use ADSP under EL2) so the dtsi that includes all 
remoteprocs being called "el1" is quite strange IMO

> Move them into sm8650-el1.dtsi, included by the EL1 boards, and drop the
> node deletions from the Deckard dts.
>
> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
> ---
>   .../boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts      |   1 +
>   arch/arm64/boot/dts/qcom/sm8650-el1.dtsi           | 775 +++++++++++++++++++++
>   arch/arm64/boot/dts/qcom/sm8650-hdk.dts            |   1 +
>   arch/arm64/boot/dts/qcom/sm8650-mtp.dts            |   1 +
>   arch/arm64/boot/dts/qcom/sm8650-qrd.dts            |   1 +
>   arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts  |  21 -
>   arch/arm64/boot/dts/qcom/sm8650.dtsi               | 774 +-------------------
>   7 files changed, 801 insertions(+), 773 deletions(-
> [..]


Thanks,
~val


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

* Re: [PATCH RFC 09/23] ASoC: qcom: lpass: add support for optional WSA interface
  2026-11-13 18:38 ` [PATCH RFC 09/23] ASoC: qcom: lpass: add support for optional WSA interface Neil Armstrong
@ 2026-11-13 19:11   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:11 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [Medium] ASoC: qcom: lpass: lpass_wsa_regmap_config uses RXTX
  callbacks and macros
- [Medium] ASoC: qcom: lpass: lpass_platform_pcmops_trigger() enables
  WSA IRQs on stop
- [Medium] ASoC: qcom: lpass: unchecked regmap_read() in
  lpass_platform_wsaif_irq()
- [Medium] ASoC: qcom: lpass: missing wsa_lpaif_map handling in suspend
  and resume
--

commit ee7ef8a5c6b3d75b7ea08d4564f71cd7fbf7abeb
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: lpass: add support for optional WSA interface

This commit adds LPASS CPU, regmap, and platform DMA support for the
optional WSA LPAIF Codec DMA interface used for speaker SoundWire buses.

> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index ea8b6083529c1..d46b72c926b2b 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
> @@ -983,6 +983,16 @@ static struct regmap_config lpass_va_regmap_config = {
>  	.cache_type = REGCACHE_FLAT,
>  };
>  
> +static struct regmap_config lpass_wsa_regmap_config = {
> +	.reg_bits = 32,
> +	.reg_stride = 4,
> +	.val_bits = 32,
> +	.writeable_reg = lpass_rxtx_regmap_writeable,
> +	.readable_reg = lpass_rxtx_regmap_readable,
> +	.volatile_reg = lpass_rxtx_regmap_volatile,
> +	.cache_type = REGCACHE_FLAT,
> +};
> +
>  static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
>  						struct device_node *node,
>  						const char *name)

[ ... ]

> @@ -1142,6 +1161,18 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>  		if (IS_ERR(drvdata->va_lpaif_map))
>  			return PTR_ERR(drvdata->va_lpaif_map);
>  
> +		if (drvdata->wsa_enable) {
> +			lpass_wsa_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant,
> +						variant->wsa_wrdma_channels +
> +						variant->wsa_wrdma_channel_start,
> +						LPASS_CDC_DMA_WSA_TX0);
> +
> +			drvdata->wsa_lpaif_map = devm_regmap_init_mmio(dev, drvdata->wsa_lpaif,
> +								       &lpass_wsa_regmap_config);
> +			if (IS_ERR(drvdata->wsa_lpaif_map))
> +				return PTR_ERR(drvdata->wsa_lpaif_map);
> +		}
> +
>  		ret = of_lpass_cdc_dma_clks_parse(dev, drvdata);
>  		if (ret) {
>  			dev_err(dev, "failed to get cdc dma clocks %d\n", ret);

[Severity: Medium]
Should lpass_wsa_regmap_config use dedicated WSA regmap callbacks instead
of the RXTX ones, and should max_register use LPAIF_CDC_WSA_WRDMAPER_REG()?

__lpass_rxtx_regmap_accessible() and lpass_rxtx_regmap_volatile() check
v->rxtx_* parameters and pass LPASS_CDC_DMA_RX0 / LPASS_CDC_DMA_TX3 to
LPAIF_CDC_RXTX_*_REG(), leaving the newly added v->wsa_irq_ports,
LPAIF_CDC_WSA_RDMA_INTF_REG(), and LPAIF_CDC_WSA_WRDMA_INTF_REG() unused.
Any difference between WSA and RXTX register geometry or channel counts
causes accesses on wsa_lpaif_map to fail with -EIO or return stale
REGCACHE_FLAT entries.  In addition, __lpass_rxtx_regmap_accessible()
omits adding wrdma_channel_start to i when checking
LPAIF_CDC_RXTX_WRDMACURR_REG(), so lpass_platform_pcmops_pointer() reads
of LPAIF_DMACURR_REG on WRDMA capture channels >= rxtx_wrdma_channels also
fail with -EIO:

sound/soc/qcom/lpass-cpu.c:__lpass_rxtx_regmap_accessible() {
    ...
	for (i = 0; i < v->rxtx_wrdma_channels; ++i) {
		if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;
    ...
		if (rw == LPASS_REG_READ) {
			if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
				return true;
		}
    ...
}

> diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
> index e162627d6f86e..9c879a8090bbf 100644
> --- a/sound/soc/qcom/lpass-platform.c
> +++ b/sound/soc/qcom/lpass-platform.c

[ ... ]

> @@ -838,6 +937,22 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
>  			val_mask = LPAIF_IRQ_ALL(ch);
>  			val_irqen = LPAIF_IRQ_ALL(ch);
>  			break;
> +		case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
> +		case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
> +			ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF);
> +			if (ret) {
> +				dev_err(soc_runtime->dev,
> +					"error writing to rdmactl reg field: %d\n", ret);
> +				return ret;
> +			}
> +
> +			reg_irqclr = LPAIF_WSA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
> +			val_irqclr = LPAIF_IRQ_ALL(ch);
> +
> +			reg_irqen = LPAIF_WSA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
> +			val_mask = LPAIF_IRQ_ALL(ch);
> +			val_irqen = LPAIF_IRQ_ALL(ch);
> +			break;
>  		default:
>  			dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
>  			return -EINVAL;

[Severity: Medium]
In the SNDRV_PCM_TRIGGER_STOP branch of lpass_platform_pcmops_trigger(),
doesn't setting val_irqen = LPAIF_IRQ_ALL(ch) keep WSA channel interrupts
enabled in LPAIF_WSA_IRQEN_REG instead of disabling them?

reg_irqclr and val_irqclr are also never written in the stop branch.  Once
lpass_platform_pcmops_close() clears drvdata->wsa_substream[data->dma_ch],
lpass_platform_wsaif_irq() ignores any pending interrupt on that channel
without clearing LPAIF_WSA_IRQCLEAR_REG while returning IRQ_HANDLED, which
can cause an interrupt storm.

[ ... ]

> @@ -1124,6 +1245,31 @@ static irqreturn_t lpass_platform_vaif_irq(int irq, void *data)
>  	return IRQ_HANDLED;
>  }
>  
> +static irqreturn_t lpass_platform_wsaif_irq(int irq, void *data)
> +{
> +	struct lpass_data *drvdata = data;
> +	const struct lpass_variant *v = drvdata->variant;
> +	unsigned int irqs;
> +	irqreturn_t rv;
> +	int chan;
> +
> +	rv = regmap_read(drvdata->wsa_lpaif_map,
> +			 LPAIF_WSA_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
> +
> +	/* Handle per channel interrupts */
> +	for (chan = 0; chan < LPASS_MAX_CDC_DMA_CHANNELS; chan++) {
> +		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->wsa_substream[chan]) {
> +			rv = lpass_dma_interrupt_handler(
> +						drvdata->wsa_substream[chan],
> +						drvdata, chan, irqs);
> +			if (rv != IRQ_HANDLED)
> +				return rv;
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}

[Severity: Medium]
What happens in lpass_platform_wsaif_irq() if regmap_read() fails?

Unlike lpass_platform_lpaif_irq() and lpass_platform_hdmiif_irq(), rv is
not checked before reading the uninitialized stack variable irqs, which
can dispatch spurious period, xrun, or bus-error events on active streams.

[ ... ]

> @@ -1359,6 +1513,34 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
>  				"error initializing va dmactl fields: %d\n", ret);
>  			return ret;
>  		}
> +
> +		if (drvdata->wsa_enable) {
> +			ret = regmap_write(drvdata->wsa_lpaif_map,
> +				LPAIF_WSA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0x0);
> +			if (ret) {
> +				dev_err(&pdev->dev, "error writing to wsa irqen reg: %d\n", ret);
> +				return ret;
> +			}
> +			drvdata->wsaif_irq = platform_get_irq_byname(pdev, "lpass-irq-wsaif");
> +			if (drvdata->wsaif_irq < 0)
> +				return -ENODEV;
> +
> +			ret = devm_request_irq(&pdev->dev, drvdata->wsaif_irq,
> +					       lpass_platform_wsaif_irq, 0,
> +					       "lpass-irq-wsaif", drvdata);
> +			if (ret) {
> +				dev_err(&pdev->dev, "wsa irq request failed: %d\n", ret);
> +				return ret;
> +			}
> +
> +			ret = lpass_platform_alloc_wsa_dmactl_fields(&pdev->dev,
> +								     drvdata->wsa_lpaif_map);
> +			if (ret) {
> +				dev_err(&pdev->dev,
> +					"error initializing wsa dmactl fields: %d\n", ret);
> +				return ret;
> +			}
> +		}
>  	}

[Severity: Medium]
Should lpass_platform_pcmops_suspend() and lpass_platform_pcmops_resume()
also handle drvdata->wsa_lpaif_map when drvdata->wsa_enable is set?

lpass_wsa_regmap_config uses REGCACHE_FLAT, but suspend and resume only
manage hdmiif_map and lpaif_map.  If LPASS loses hardware state across
system suspend, cached WSA DMA registers are not synced on resume, and
subsequent regmap_update_bits() or regmap_fields_write() calls see the
pre-suspend values in the flat cache and skip writing to hardware.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=9

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

* Re: [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi
  2026-11-13 18:38 ` [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi Neil Armstrong
  2026-11-13 19:08   ` Val Packett
@ 2026-11-13 19:11   ` sashiko-bot
  1 sibling, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:11 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] arm64: dts: qcom: sm8650: lpass_vamacro left enabled with
  zero clocks
- [Low] arm64: dts: qcom: sm8650: leftover video_mem reference on iris
  in sm8650.dtsi
--

commit 6cf38f344aa0427c5afd87fe6828f76c27873b93
Author: Neil Armstrong <neil.armstrong@linaro.org>

arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi

This commit moves the Gunyah EL1-specific reserved memory, remoteproc,
SMP2P, IPA, GPU zap shader, and q6prm LPASS clock nodes out of sm8650.dtsi
into sm8650-el1.dtsi, and removes the corresponding node deletions from
sm8650-valve-deckard.dts.

> diff --git a/arch/arm64/boot/dts/qcom/sm8650-el1.dtsi b/arch/arm64/boot/dts/qcom/sm8650-el1.dtsi
> new file mode 100644
> index 0000000000000..18b7c18ae3120
> --- /dev/null
> +++ b/arch/arm64/boot/dts/qcom/sm8650-el1.dtsi

[ ... ]

> +&iris {
> +	memory-region = <&video_mem>;
> +};

[Severity: Low]
Was memory-region = <&video_mem>; meant to be removed from the iris node in
sm8650.dtsi?

Now that video_mem lives in sm8650-el1.dtsi, leaving memory-region =
<&video_mem>; in sm8650.dtsi duplicates the property on EL1 boards and
causes an unresolved phandle build error on any EL2 board that does not
define its own video_mem label.

> diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi
> index 86fbb0fd6472d..d88acdb8c7759 100644
> --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi

[ ... ]

> @@ -4854,12 +4302,9 @@ swr2: soundwire@6d30000 {
>  		lpass_vamacro: codec@6d44000 {
>  			compatible = "qcom,sm8650-lpass-va-macro", "qcom,sm8550-lpass-va-macro";
>  			reg = <0 0x06d44000 0 0x1000>;
> -			clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
> -				 <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
> -				 <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
> -			clock-names = "mclk",
> -				      "macro",
> -				      "dcodec";
> +
> +			clocks = <0>, <0>, <0>;
> +			clock-names = "mclk", "macro", "dcodec";
>  
>  			#clock-cells = <0>;
>  			clock-output-names = "fsgen";

[Severity: Medium]
Should lpass_vamacro also have status = "disabled" here, like the other
five LPASS macro and pinctrl nodes?

Because /delete-node/ &lpass_vamacro; is dropped from
sm8650-valve-deckard.dts and sm8650-el1.dtsi explicitly sets status =
"okay" on &lpass_vamacro, leaving it enabled in sm8650.dtsi with
placeholder clocks = <0>, <0>, <0> causes va_macro_probe() ->
devm_clk_get(dev, "mclk") to fail with -ENOENT at boot on EL2 boards that
do not override the clocks, and breaks dtbs_check schema validation.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=20

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

* Re: [PATCH RFC 19/23] ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu
  2026-11-13 18:38 ` [PATCH RFC 19/23] ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu Neil Armstrong
@ 2026-11-13 19:12   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:12 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] ASoC: qcom: sc8280xp: DAI ID collision in
  sc8280xp_snd_hw_params()
- [Medium] ASoC: qcom: sc8280xp: DAI ID collision in
  sc8280xp_be_hw_params_fixup()
- [Medium] ASoC: qcom: sc8280xp: missing lpass-cpu jack DAI IDs in
  sc8280xp_snd_init()
--

commit 3e03c7d28b8733d89a72bc91c60e7c4086af115e
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu

This commit adds LPASS Codec DMA DAI IDs and attaches DAI link callbacks
unconditionally so the sc8280xp machine driver can be used with
lpass-cpu DAI links.

> diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
> index 8cc75a7ad866d..ff57fb9051a26 100644
> --- a/sound/soc/qcom/sc8280xp.c
> +++ b/sound/soc/qcom/sc8280xp.c

[ ... ]

> @@ -242,6 +243,8 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
>  	switch (cpu_dai->id) {
>  	case WSA_CODEC_DMA_RX_0:
>  	case WSA_CODEC_DMA_RX_1:
> +	case LPASS_CDC_DMA_WSA_RX0:
> +	case LPASS_CDC_DMA_WSA_RX1:
>  		/*
>  		 * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume
>  		 * to reduce the risk of speaker damage until we have active

[Severity: Medium]
Does switching on cpu_dai->id here without checking rtd->dai_link->no_pcm
collide with Q6DSP TDM DAI IDs, while leaving DisplayPort and WCD headset
jack setup broken on lpass-cpu links?

LPASS_CDC_DMA_WSA_RX0 and LPASS_CDC_DMA_WSA_RX1 (36 and 37) have the same
values as PRIMARY_TDM_RX_6 and PRIMARY_TDM_TX_6 on AudioReach DPCM links.
Meanwhile, for lpass-cpu links, LPASS_DP_RX is missing from the
DisplayPort cases below (skipping qcom_snd_dp_jack_setup()), and
qcom_snd_wcd_jack_setup() only attaches the jack to the codec component
for LPI_MI2S_RX_0 and TX_CODEC_DMA_TX_0..3, ignoring
LPASS_CDC_DMA_TX0..3.

> @@ -323,6 +326,10 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
>  		}
>  		break;
>  	}
> +	case LPASS_CDC_DMA_TX0:
> +	case LPASS_CDC_DMA_TX1:
> +	case LPASS_CDC_DMA_TX2:
> +	case LPASS_CDC_DMA_TX3:
>  	case TX_CODEC_DMA_TX_0:
>  	case TX_CODEC_DMA_TX_1:
>  	case TX_CODEC_DMA_TX_2:

[Severity: Medium]
Does adding LPASS_CDC_DMA_TX0..LPASS_CDC_DMA_TX3 to
sc8280xp_be_hw_params_fixup() alter PRIMARY_MI2S and SECONDARY_MI2S DPCM
backends instead of affecting lpass-cpu links?

In sc8280xp_add_be_ops(), sc8280xp_be_hw_params_fixup() is only wired up
when link->no_pcm == 1 (AudioReach DPCM backend links), so it never runs
on non-DPCM lpass-cpu links.  On DPCM links where it does run,
LPASS_CDC_DMA_TX0..LPASS_CDC_DMA_TX3 (16..19) alias PRIMARY_MI2S_RX,
PRIMARY_MI2S_TX, SECONDARY_MI2S_RX and SECONDARY_MI2S_TX (16..19),
changing their minimum channel constraint from 2 to 1.

> @@ -533,12 +540,11 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
>  	int i;
>  
>  	for_each_card_prelinks(card, i, link) {
> -		if (link->no_pcm == 1) {
> -			link->init = sc8280xp_snd_init;
> -			link->exit = sc8280xp_snd_exit;
> +		link->init = sc8280xp_snd_init;
> +		link->exit = sc8280xp_snd_exit;
> +		link->ops = &sc8280xp_be_ops;
> +		if (link->no_pcm == 1)
>  			link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup;
> -			link->ops = &sc8280xp_be_ops;
> -		}
>  	}
>  }

[Severity: High]
When sc8280xp_be_ops is attached to non-DPCM lpass-cpu links, does
sc8280xp_snd_hw_params() misidentify LPASS_CDC_DMA_TX0..7 as MI2S DAIs
and fail stream setup with -ENOTSUPP?

sound/soc/qcom/sc8280xp.c:sc8280xp_snd_hw_params() {
    ...
	switch (cpu_dai->id) {
	case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
    ...
		if (mi2s_mclk_enable) {
			ret = snd_soc_dai_set_sysclk(cpu_dai,
						     LPAIF_MI2S_MCLK, mclk_freq,
						     SND_SOC_CLOCK_OUT);
			if (ret)
				return ret;
		}
    ...
	case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
	case AIF_TDM_RX_0 ... AIF_TDM_TX_12:
		return sc8280xp_tdm_hw_params(substream, params);
    ...
}

LPASS_CDC_DMA_TX0..LPASS_CDC_DMA_TX7 (16..23) share the same numeric IDs
as PRIMARY_MI2S_RX..QUATERNARY_MI2S_TX (16..23).  On platforms such as
sm8650 where mi2s_mclk_enable and mi2s_bclk_enable are set in
sm8650_priv_data, starting a capture stream on LPASS_CDC_DMA_TX0..7 calls
snd_soc_dai_set_sysclk() on the LPASS Codec DMA DAI, which does not
implement .set_sysclk and returns -ENOTSUPP.

Conversely, actual lpass-cpu MI2S DAI IDs do not match the Q6DSP MI2S
ranges: MI2S_PRIMARY..MI2S_QUINARY (0..4) hit the default case and skip
format and clock setup, while MI2S_SENARY..MI2S_SEPTENARY (34..35) fall
into PRIMARY_TDM_RX_0..QUINARY_TDM_TX_7 (24..103) and are routed to
sc8280xp_tdm_hw_params().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=19

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

* Re: [PATCH RFC 15/23] ASoC: qcom: lpass: allocate LPM buffer memory to DAIs
  2026-11-13 18:38 ` [PATCH RFC 15/23] ASoC: qcom: lpass: allocate LPM buffer memory to DAIs Neil Armstrong
@ 2026-11-13 19:12   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:12 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 5 potential
issues to consider:
- [High] ASoC: qcom: lpass: u32 underflow and OOB in
  lpass_platform_allocate_lpm()
- [Medium] ASoC: qcom: lpass: uninitialized capture buffer on
  full-duplex LPM MI2S PCM
- [Medium] ASoC: qcom: lpass: unconditional VA LPM reservation blocks VA
  LPM MI2S
- [Medium] ASoC: qcom: lpass: missing LPM clock parsing in
  asoc_qcom_lpass_cpu_platform_probe()
- [Low] ASoC: qcom: lpass: wrong LPM offset in
  lpass_platform_prealloc_cdc_dma_buffer()
--

commit 038ab20def13fb9da7aca847692783be3d4964b4
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: lpass: allocate LPM buffer memory to DAIs

This commit dynamically allocates LPASS LPM SRAM buffer offsets for Codec
DMA and non-core LPAIF MI2S DAIs instead of using fixed static offsets.

> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index 9ebbc422c63c3..4bbcd59ebd602 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
> @@ -1258,16 +1258,6 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>  			dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
>  			return ret;
>  		}
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
> -		if (!res)
> -			return -EINVAL;
> -		drvdata->rxtx_cdc_dma_lpm_buf = res->start;
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
> -		if (!res)
> -			return -EINVAL;
> -		drvdata->va_cdc_dma_lpm_buf = res->start;
>  	}
>  
>  	if (variant->mi2s_desc) {
> @@ -1281,6 +1271,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>  						   variant->mi2s_desc[i].reg_name);
>  			if (ret)
>  				return ret;
> +
> +			if (variant->mi2s_desc[i].lpm_type != LPASS_LPM_NONE)
> +				drvdata->mi2s_lpm_enable = true;
>  		}
>  	} else {
>  		drvdata->num_lpaif = 1;
> @@ -1290,6 +1283,20 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
>  			return ret;
>  	}
>  
> +	if (drvdata->codec_dma_enable || drvdata->mi2s_lpm_enable) {
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
> +		if (!res)
> +			return -EINVAL;
> +		drvdata->rxtx_cdc_dma_lpm_buf = res->start;
> +		drvdata->rxtx_cdc_dma_lpm_size = resource_size(res);
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
> +		if (!res)
> +			return -EINVAL;
> +		drvdata->va_cdc_dma_lpm_buf = res->start;
> +		drvdata->va_cdc_dma_lpm_size = resource_size(res);
> +	}

[Severity: Medium]
Should of_lpass_cdc_dma_clks_parse() also be called when
drvdata->mi2s_lpm_enable is set?

Because of_lpass_cdc_dma_clks_parse() remains inside the
if (drvdata->codec_dma_enable) block above, a board that enables LPM-backed
MI2S DAIs without any Codec DMA DAI leaves drvdata->codec_mem0..3 and
drvdata->va_mem0 as NULL.  Even with commit "ASoC: qcom: lpass: enable the
right codec_mem clock when usin LPM memory" later in this series,
lpass_cpu_daiops_startup() will pass a NULL clk pointer to
clk_prepare_enable(), leaving the LPM SRAM unclocked when accessed by the
CPU or LPAIF DMA.

> diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
> index a953db262aa00..75d2c872f5123 100644
> --- a/sound/soc/qcom/lpass-platform.c
> +++ b/sound/soc/qcom/lpass-platform.c

[ ... ]

> @@ -1315,23 +1348,55 @@ static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *comp
>  	switch (dai_id) {
>  	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
>  		buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
> -		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf;
> +		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + drvdata->va_lpm_offset;
>  		break;
>  	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
>  		buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
> -		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
> +		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
> +				drvdata->va_lpm_offset + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
>  		break;

[Severity: Low]
Should these two cases use drvdata->rxtx_lpm_offset instead of
drvdata->va_lpm_offset?  lpass_platform_allocate_lpm() populates
drvdata->rxtx_lpm_offset for RXTX Codec DMA, and commit "ASoC: qcom: lpass:
enable the right codec_mem clock when usin LPM memory" uses
drvdata->rxtx_lpm_offset to select the RXTX LPM clock, so using
va_lpm_offset here will desynchronize the buffer address from the clock if
the two offsets ever differ.

>  	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
>  		buf->bytes = lpass_platform_va_hardware.buffer_bytes_max;
> -		buf->addr = drvdata->va_cdc_dma_lpm_buf;
> +		buf->addr = drvdata->va_cdc_dma_lpm_buf + drvdata->va_lpm_offset;
>  		break;
>  	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
>  		buf->bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
> -		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + drvdata->wsa_lpm_offset;
>  		break;
>  	case LPASS_CDC_DMA_WSA_TX0 ... LPASS_CDC_DMA_WSA_TX8:
>  		buf->bytes = lpass_platform_wsa_hardware.buffer_bytes_max;
> -		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 3;
> +		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
> +				drvdata->wsa_lpm_offset + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
> +		break;
> +	case MI2S_PRIMARY ... MI2S_QUINARY:
> +	case MI2S_SENARY ... MI2S_SEPTENARY:
> +		if (is_lpm_mi2s_port(drvdata, dai_id)) {
> +			unsigned int port_id = mi2s_port_id(dai_id);
> +
> +			buf->bytes = lpass_platform_lpm_mi2s_hardware.buffer_bytes_max;
> +			switch (drvdata->mi2s_lpm[port_id]) {
> +			case LPASS_RXTX_CDC_LPM:
> +				if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
> +					buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
> +						    drvdata->mi2s_lpm_offset[port_id];
> +				else
> +					buf->addr = drvdata->rxtx_cdc_dma_lpm_buf +
> +						    drvdata->mi2s_lpm_offset[port_id] +
> +						    LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
> +				break;
> +			case LPASS_VA_CDC_LPM:
> +				if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
> +					buf->addr = drvdata->va_cdc_dma_lpm_buf +
> +						    drvdata->mi2s_lpm_offset[port_id];
> +				else
> +					buf->addr = drvdata->va_cdc_dma_lpm_buf +
> +						    drvdata->mi2s_lpm_offset[port_id] +
> +						    LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
> +				break;
> +			default:
> +				break;
> +			}
> +		}
>  		break;
>  	default:
>  		break;

[Severity: Medium]
Does lpass_platform_prealloc_cdc_dma_buffer() leave the capture
substream->dma_buffer uninitialized when an LPM MI2S PCM has both playback
and capture streams?

At the top of lpass_platform_prealloc_cdc_dma_buffer(), only one substream
is selected:

sound/soc/qcom/lpass-platform.c:lpass_platform_prealloc_cdc_dma_buffer() {
	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
		substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
	else
		substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;

	buf = &substream->dma_buffer;
...
}

Unlike Codec DMA DAIs (which have separate unidirectional RX and TX DAI
IDs), a single MI2S DAI can have both playback and capture substreams on
the same struct snd_pcm.  When both exist, only the playback dma_buffer is
initialized and memremapped, while the capture substream->dma_buffer stays
zeroed.  Opening the capture stream in lpass_platform_pcmops_open() then
sets runtime->dma_area to NULL via snd_pcm_set_runtime_buffer(), causing a
NULL pointer dereference in lpass_platform_copy().

[ ... ]

> @@ -1464,6 +1527,64 @@ static const struct snd_soc_component_driver lpass_component_driver = {
>  
>  };
>  
> +static int lpass_platform_allocate_lpm(struct lpass_data *drvdata)
> +{
> +	u32 rxtx_cdc_dma_lpm_remain = drvdata->rxtx_cdc_dma_lpm_size;
> +	u32 va_cdc_dma_lpm_remain = drvdata->va_cdc_dma_lpm_size;
> +	u32 rxtx_cdc_dma_lpm_offset = 0;
> +	u32 va_cdc_dma_lpm_offset = 0;
> +	int port_id;
> +
> +	if (drvdata->codec_dma_enable) {
> +		drvdata->rxtx_lpm_offset = rxtx_cdc_dma_lpm_offset;
> +		rxtx_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +		rxtx_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +
> +		drvdata->va_lpm_offset = va_cdc_dma_lpm_offset;
> +		va_cdc_dma_lpm_offset += LPASS_VA_CDC_DMA_LPM_BUFF_SIZE;
> +		va_cdc_dma_lpm_remain -= LPASS_VA_CDC_DMA_LPM_BUFF_SIZE;
> +
> +		if (drvdata->wsa_enable) {
> +			drvdata->wsa_lpm_offset = rxtx_cdc_dma_lpm_offset;
> +			rxtx_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +			rxtx_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +		}
> +	}

[Severity: Medium]
Does reserving LPASS_VA_CDC_DMA_LPM_BUFF_SIZE whenever
drvdata->codec_dma_enable is set prevent using LPASS_VA_CDC_LPM for MI2S
when only RXTX or WSA Codec DMA is enabled?

of_lpass_cpu_parse_dai_data() sets drvdata->codec_dma_enable = 1 for any
RXTX, WSA, or VA Codec DMA DAI.  Because the hardware lpass-va-cdc-dma-lpm
region is 12 KiB (LPASS_VA_CDC_DMA_LPM_BUFF_SIZE), subtracting
LPASS_VA_CDC_DMA_LPM_BUFF_SIZE here even when no VA Codec DMA DAI is in DT
reduces va_cdc_dma_lpm_remain to 0, causing the LPASS_VA_CDC_LPM check
below to always fail with -EINVAL.

> +
> +	if (!drvdata->mi2s_lpm_enable)
> +		return 0;
> +
> +	for (port_id = 0; port_id < LPASS_MAX_MI2S_PORTS; port_id++) {
> +		if (!test_bit(port_id, &drvdata->lpaif_port_enable))
> +			continue;
> +
> +		switch (drvdata->mi2s_lpm[port_id]) {
> +		case LPASS_RXTX_CDC_LPM:
> +			if (rxtx_cdc_dma_lpm_remain < LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE)
> +				return -EINVAL;
> +			drvdata->mi2s_lpm_offset[port_id] = rxtx_cdc_dma_lpm_offset;
> +			rxtx_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +			rxtx_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +			break;
> +		case LPASS_VA_CDC_LPM:
> +			/*
> +			 * VA Codec uses 12KiB, but MI2S uses 8KiB
> +			 * Meaning only VA _or_ a single MI2S can be enabled
> +			 */
> +			if (va_cdc_dma_lpm_remain < LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE)
> +				return -EINVAL;
> +			drvdata->mi2s_lpm_offset[port_id] = va_cdc_dma_lpm_offset;
> +			va_cdc_dma_lpm_offset += LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +			va_cdc_dma_lpm_remain -= LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2;
> +			break;

[Severity: High]
Can rxtx_cdc_dma_lpm_remain and va_cdc_dma_lpm_remain underflow and assign
out-of-bounds LPM buffer offsets here?

Both cases check for LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE (8 KiB) remaining,
but then advance the offset and decrement the unsigned u32 remaining
counter by LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE * 2 (16 KiB).  On a 12 KiB
lpass-va-cdc-dma-lpm region (0x3000 bytes), enabling one MI2S port on
LPASS_VA_CDC_LPM passes the < 0x2000 check and subtracts 0x4000, wrapping
va_cdc_dma_lpm_remain to 0xfffff000:

1. If a second MI2S port on LPASS_VA_CDC_LPM is enabled (such as both
   MI2S_QUATERNARY and MI2S_QUINARY), 0xfffff000 < 0x2000 is false, so it
   is given an offset 16 KiB past the start of the 12 KiB VA LPM region.
2. Even with a single MI2S port on LPASS_VA_CDC_LPM, a capture stream in
   lpass_platform_prealloc_cdc_dma_buffer() is placed at
   mi2s_lpm_offset[port_id] + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE (8 KiB ..
   16 KiB), overflowing the 12 KiB VA LPM region by 4 KiB during
   memremap(), mmap(), and DMA.
3. The same u32 underflow occurs on LPASS_RXTX_CDC_LPM whenever 8 KiB
   remains before subtracting 16 KiB.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=15

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

* Re: [PATCH RFC 02/23] clk: qcom: add SM8650 LPASS core & audio clock controllers
  2026-11-13 18:38 ` [PATCH RFC 02/23] clk: " Neil Armstrong
@ 2026-11-13 19:14   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:14 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 5 potential
issues to consider:
- [High] clk: qcom: missing pm_clk setup for iface clock in
  lpass_hm_core_probe()
- [High] clk: qcom: wrong DT clock index for BI_TCXO in
  lpass_core_cc_parent_data_ao_2
- [Medium] clk: qcom: missing driver unregister on error in SM8650 LPASS
  init functions
- [Medium] clk: qcom: wrong postdiv clk_ops and user_ctl_val for
  lpass_core_cc_dig_pll
- [Low] clk: qcom: dead code, unused declaration, and unwrapped pm_ops
  in SM8650 LPASS
--

commit b45bde29cb5ec941ad6de37d77c22a656fbda080
Author: Neil Armstrong <neil.armstrong@linaro.org>

clk: qcom: add SM8650 LPASS core & audio clock controllers

This commit adds clock and power domain controller drivers for the SM8650
LPASS HM, core, audio, and AON blocks when running without ADSP firmware.

> diff --git a/drivers/clk/qcom/lpassaudiocc-sm8650.c b/drivers/clk/qcom/lpassaudiocc-sm8650.c
> new file mode 100644
> index 0000000000000..636ce9a1b3451
> --- /dev/null
> +++ b/drivers/clk/qcom/lpassaudiocc-sm8650.c
[ ... ]
> +static struct clk_rcg2 lpass_aon_cc_ext_if1_clk_src = {
> +	.cmd_rcgr = 0xb004,
> +	.mnd_width = 16,
> +	.hid_width = 5,
> +	.parent_map = lpass_audio_cc_parent_map_0,
> +	.freq_tbl = ftbl_lpass_cc_ext_clk_src,
> +	.clkr.hw.init = &(const struct clk_init_data){
> +		.name = "lpass_aon_cc_ext_if1_clk_src",
> +		.parent_data = lpass_aon_cc_parent_data_0,
> +		.num_parents = ARRAY_SIZE(lpass_aon_cc_parent_data_0),
> +		.ops = &clk_rcg2_ops,
> +	},
> +};
[ ... ]
> +static struct clk_rcg2 lpass_aon_cc_tx_mclk_clk_src = {
> +	.cmd_rcgr = 0x1b004,
> +	.mnd_width = 0,
> +	.hid_width = 5,
> +	.parent_map = lpass_aon_cc_parent_map_0,
> +	.freq_tbl = ftbl_lpass_mclk_clk_src,
> +	.clkr.hw.init = &(const struct clk_init_data){
> +		.name = "lpass_aon_cc_tx_mclk_clk_src",
> +		.parent_data = lpass_aon_cc_parent_data_0,
> +		.num_parents = ARRAY_SIZE(lpass_aon_cc_parent_data_0),
> +		//.flags = CLK_OPS_PARENT_ENABLE,
> +		.ops = &clk_rcg2_ops,
> +	},
> +};
[ ... ]
> +static struct clk_branch lpass_aon_cc_tx_mclk_clk;
[ ... ]
> +static const struct dev_pm_ops lpass_audio_cc_sm8650_pm_ops = {
> +	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
> +};
> +
> +static struct platform_driver lpass_audio_cc_sm8650_driver = {
> +	.probe = lpass_audio_cc_sm8650_probe,
> +	.driver = {
> +		.name = "lpass_audio_cc-sm8650",
> +		.of_match_table = lpass_audio_cc_sm8650_match_table,
> +		.pm = &lpass_audio_cc_sm8650_pm_ops,
> +	},
> +};

[Severity: Low]
This isn't a bug, but the `.pm` assignments in `lpass_audio_cc_sm8650_driver`,
`lpass_aon_cc_sm8650_driver`, and `lpass_hm_sm8650_driver` do not use
`pm_ptr()` (with `RUNTIME_PM_OPS()`), so the `dev_pm_ops` structures are not
dropped when PM is disabled.

Also, `lpass_aon_cc_tx_mclk_clk_src` has a commented-out `.flags` line, the
forward declaration of `lpass_aon_cc_tx_mclk_clk` is unused because the
following clocks reference `lpass_aon_cc_tx_mclk_clk_src`,
`lpass_aon_cc_ext_if1_clk_src` uses `lpass_audio_cc_parent_map_0` instead of
`lpass_aon_cc_parent_map_0`, and `lpass_audio_cc_sm8650_probe()` and
`lpass_aon_cc_sm8650_probe()` duplicate the same `pm_clk` setup.

[ ... ]
> +static int __init lpass_audio_cc_sm8650_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_driver_register(&lpass_aon_cc_sm8650_driver);
> +	if (ret)
> +		return ret;
> +
> +	return platform_driver_register(&lpass_audio_cc_sm8650_driver);
> +}
> +subsys_initcall(lpass_audio_cc_sm8650_init);

[Severity: Medium]
Does `lpass_audio_cc_sm8650_init()` leave `lpass_aon_cc_sm8650_driver`
registered if registering `lpass_audio_cc_sm8650_driver` fails?  When built as
a module, failing module init without calling
`platform_driver_unregister(&lpass_aon_cc_sm8650_driver)` leaves a freed
`platform_driver` structure linked on the platform bus list, causing a
use-after-free.  `lpass_core_cc_sm8650_init()` in `lpasscorecc-sm8650.c` has
the same issue with `lpass_hm_sm8650_driver`.

> diff --git a/drivers/clk/qcom/lpasscorecc-sm8650.c b/drivers/clk/qcom/lpasscorecc-sm8650.c
> new file mode 100644
> index 0000000000000..4f91ab0d772ec
> --- /dev/null
> +++ b/drivers/clk/qcom/lpasscorecc-sm8650.c
> @@ -0,0 +1,509 @@
[ ... ]
> +static const struct alpha_pll_config lpass_core_cc_dig_pll_config = {
> +	/* 614.4 MHz */
> +	.l = 0x20,
> +	.alpha = 0x0,
> +	.config_ctl_val = 0x20485699,
> +	.config_ctl_hi_val = 0x00182261,
> +	.config_ctl_hi1_val = 0x82aa299c,
> +	.test_ctl_val = 0x00000000,
> +	.test_ctl_hi_val = 0x00000003,
> +	.test_ctl_hi1_val = 0x00009000,
> +	.test_ctl_hi2_val = 0x00000034,
> +	.user_ctl_val = 0x00000000,
> +	.user_ctl_hi_val = 0x00000005,
> +};
[ ... ]
> +static struct clk_alpha_pll_postdiv lpass_core_cc_dig_pll_out_odd = {
> +	.offset = 0x0,
> +	.post_div_shift = 14,
> +	.post_div_table = post_div_table_lpass_core_cc_dig_pll_out_odd,
> +	.num_post_div = ARRAY_SIZE(post_div_table_lpass_core_cc_dig_pll_out_odd),
> +	.width = 4,
> +	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE],
> +	.clkr.hw.init = &(struct clk_init_data){
> +		.name = "lpass_core_cc_dig_pll_out_odd",
> +		.parent_hws = (const struct clk_hw*[]){
> +			&lpass_core_cc_dig_pll.clkr.hw,
> +		},
> +		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
> +		.ops = &clk_alpha_pll_postdiv_lucid_ops,
> +	},
> +};

[Severity: Medium]
Should `lpass_core_cc_dig_pll_out_odd` use `clk_alpha_pll_postdiv_lucid_ole_ops`
instead of `clk_alpha_pll_postdiv_lucid_ops`?

`lpass_core_cc_dig_pll` is a Lucid OLE PLL, where the FSM vote bit lives at
bit 25 of `user_ctl` (`clk_lucid_evo_pll_postdiv_set_rate()`), whereas
`clk_alpha_pll_postdiv_lucid_ops` checks bit 20 of `mode`
(`clk_alpha_pll_postdiv_fabia_set_rate()`).  In addition,
`lpass_core_cc_dig_pll_config` sets `.user_ctl_val = 0x00000000`, so
`clk_alpha_pll_write_config()` skips writing `user_ctl` and never programs the
odd post-divider or output enable bits during PLL configuration.

[ ... ]
> +static const struct parent_map lpass_core_cc_parent_map_2[] = {
> +	{ P_BI_TCXO, 0 },
> +	{ P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN, 1 },
> +	{ P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC, 2 },
> +};
> +
> +static const struct clk_parent_data lpass_core_cc_parent_data_ao_2[] = {
> +	{ .index = 1 },
> +	{ .hw = &lpass_core_cc_dig_pll.clkr.hw },
> +	{ .hw = &lpass_core_cc_dig_pll_out_main_div_clk_src.clkr.hw },
> +};
> +
> +static const struct freq_tbl ftbl_lpass_core_cc_core_clk_src[] = {
> +	F(19200000, P_BI_TCXO, 1, 0, 0),
> +	F(51200000, P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC, 6, 0, 0),
> +	F(102400000, P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN_DIV_CLK_SRC, 3, 0, 0),
> +	F(204800000, P_LPASS_CORE_CC_DIG_PLL_OUT_MAIN, 3, 0, 0),
> +	{ }
> +};
> +
> +static struct clk_rcg2 lpass_core_cc_core_clk_src = {
> +	.cmd_rcgr = 0x25000,
> +	.mnd_width = 8,
> +	.hid_width = 5,
> +	.parent_map = lpass_core_cc_parent_map_2,
> +	.freq_tbl = ftbl_lpass_core_cc_core_clk_src,
> +	.clkr.hw.init = &(const struct clk_init_data){
> +		.name = "lpass_core_cc_core_clk_src",
> +		.parent_data = lpass_core_cc_parent_data_ao_2,
> +		.num_parents = ARRAY_SIZE(lpass_core_cc_parent_data_ao_2),
> +		.ops = &clk_rcg2_shared_ops,
> +	},
> +};

[Severity: High]
Should `lpass_core_cc_parent_data_ao_2[0]` use `.index = 0` instead of
`.index = 1`?

`qcom,sm8650-lpasscorecc` only has a single DT input clock (`bi_tcxo` at index
0), as used by `lpass_core_cc_dig_pll` and `lpass_core_cc_parent_data_0`.
With `.index = 1`, parent 0 of `lpass_core_cc_core_clk_src` cannot be
resolved, so `lpass_core_cc_core_clk_src` and its child branches report rate 0
when parked on `bi_tcxo` via `clk_rcg2_shared_ops`, and requesting 19.2 MHz
fails with `-EINVAL` in `_freq_tbl_determine_rate()`.

[ ... ]
> +static int lpass_hm_core_probe(struct platform_device *pdev)
> +{
> +	return qcom_cc_probe(pdev, &lpass_core_hm_sm8650_desc);
> +}
> +
> +static const struct dev_pm_ops lpass_hm_sm8650_pm_ops = {
> +	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
> +};

[Severity: High]
Does `lpass_hm_core_probe()` need to set up `pm_clk` and add the `"iface"`
clock before calling `qcom_cc_probe()`, like `lpass_audio_cc_sm8650_probe()`
and `lpass_aon_cc_sm8650_probe()` do?

`qcom_cc_really_probe()` with `.use_rpm = true` only calls
`devm_pm_runtime_enable()` and `pm_runtime_resume_and_get()`; it does not call
`devm_pm_clk_create()` or `pm_clk_add(&pdev->dev, "iface")`.  Without that
setup, `pm_clk_resume()` and `pm_clk_suspend()` in `lpass_hm_sm8650_pm_ops` do
nothing, leaving the LPASS HM interface clock unacquired and potentially gated
when `qcom_cc_really_probe()` and `gdsc_register()` access the HM registers.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=2

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

* Re: [PATCH RFC 21/23] arm64: dts: qcom: add SM8650 LPASS CPU dtsi
  2026-11-13 18:38 ` [PATCH RFC 21/23] arm64: dts: qcom: add SM8650 LPASS CPU dtsi Neil Armstrong
@ 2026-11-13 19:18   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:18 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [Medium] arm64: dts: qcom: unsupported power-domains on WSA macros in
  sm8650-lpass-cpu
- [Medium] arm64: dts: qcom: unattached multiple power-domains on
  lpass_tlmm
- [Medium] arm64: dts: qcom: lpass_core clock count mismatches
  lpasscorecc-sm8650 driver
- [Low] arm64: dts: qcom: node and property ordering in
  sm8650-lpass-cpu.dtsi
--

commit 1f3d7332ea1210e666215d104062d22d42ebae2c
Author: Neil Armstrong <neil.armstrong@linaro.org>

arm64: dts: qcom: add SM8650 LPASS CPU dtsi

This commit adds sm8650-lpass-cpu.dtsi with the LPASS clock controllers,
lpass-cpu node, LPASS macro clocks, and I2S2 pinctrl state for SM8650
boards booting at EL2 without ADSP audio firmware.

> diff --git a/arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi b/arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi
> new file mode 100644
> index 0000000000000..43d0d8daa4fe2
> --- /dev/null
> +++ b/arch/arm64/boot/dts/qcom/sm8650-lpass-cpu.dtsi

[ ... ]

> +&lpass_tlmm {
> +	clocks = <&lpass_core LPASS_CORE_CC_CORE_CLK>;
> +	clock-names = "core";
> +
> +	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
> +			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
> +	power-domain-names = "core",
> +			     "audio";
> +
> +	status = "okay";
> +};

[Severity: Medium]
Does specifying two entries in power-domains leave both GDSCs unattached on
lpass_tlmm?

genpd_dev_pm_attach() returns early without attaching any domain when a node
lists more than one power domain, and lpi_pinctrl_probe() only sets up PM
clocks without calling devm_pm_domain_attach_list() or
dev_pm_domain_attach_by_name().  As a result, runtime PM resume of
lpass_tlmm will not power on LPASS_AON_CC_LPASS_AUDIO_HM_GDSC before
accessing LPI TLMM registers.

[ ... ]

> +&lpass_wsa2macro {
> +	clocks = <&lpass_audiocc LPASS_AUDIO_CC_WSA2_MCLK_CLK>,
> +		 <&lpass_audiocc LPASS_AUDIO_CC_TX_MCLK_WSA2_CLK>,
> +		 <&lpass_vamacro>;
> +	clock-names = "mclk",
> +		      "macro",
> +		      "fsgen";
> +
> +	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
> +			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
> +	power-domain-names = "macro",
> +			     "dcodec";
> +};
> +
> +&lpass_wsamacro {
> +	clocks = <&lpass_audiocc LPASS_AUDIO_CC_WSA_MCLK_CLK>,
> +		 <&lpass_audiocc LPASS_AUDIO_CC_TX_MCLK_WSA_CLK>,
> +		 <&lpass_vamacro>;
> +	clock-names = "mclk",
> +		      "macro",
> +		      "fsgen";
> +
> +	power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>,
> +			<&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
> +	power-domain-names = "macro",
> +			     "dcodec";
> +};

[Severity: Medium]
Are power-domains and power-domain-names supported on lpass_wsa2macro and
lpass_wsamacro?

Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml does not
define power-domains or power-domain-names and sets unevaluatedProperties:
false, so dtbs_check fails when sm8650-lpass-cpu.dtsi is included.  In
addition, unlike the rx, tx, and va macro drivers, wsa_macro_probe() never
calls lpass_macro_pds_init(), while genpd_dev_pm_attach() skips automatic
attachment when more than one domain is listed, leaving both GDSCs
unattached during register access.

> +
> +&soc {
> +	lpass_audiocc: clock-controller@6bc0000 {
> +		reg = <0 0x06bc0000 0 0x38000>;
> +		compatible = "qcom,sm8650-lpassaudiocc";
> +
> +		clocks = <&bi_tcxo_div2>,
> +			 <&lpass_aon LPASS_AON_CC_MAIN_RCG_CLK_SRC>;
> +		clock-names = "bi_tcxo",
> +			      "iface";
> +
> +		power-domains = <&lpass_aon LPASS_AON_CC_LPASS_AUDIO_HM_GDSC>;
> +
> +		#clock-cells = <1>;
> +	};
> +
> +	lpass_aon: clock-controller@6e00000 {
> +		reg = <0 0x06e00000 0 0x30000>;
> +		compatible = "qcom,sm8650-lpassaoncc";
> +
> +		clocks = <&bi_tcxo_div2>,
> +			 <&lpass_core LPASS_CORE_CC_CORE_CLK>;
> +		clock-names = "bi_tcxo",
> +			      "iface";
> +
> +		#clock-cells = <1>;
> +		#power-domain-cells = <1>;
> +	};
> +
> +	lpass_core: clock-controller@7b00000 {
> +		compatible = "qcom,sm8650-lpasscorecc";
> +		reg = <0 0x07b00000 0 0x58000>;
> +
> +		clocks = <&bi_tcxo_div2>;
> +		clock-names = "bi_tcxo";
> +
> +		power-domains = <&lpass_hm LPASS_CORE_HM_GDSC>;
> +
> +		#clock-cells = <1>;
> +	};

[Severity: Medium]
Does providing only a single clock at index 0 on lpass_core leave the
P_BI_TCXO parent of lpass_core_cc_core_clk_src unresolved?

In drivers/clk/qcom/lpasscorecc-sm8650.c, lpass_core_cc_parent_data_ao_2
looks up DT clock index 1 for P_BI_TCXO:

drivers/clk/qcom/lpasscorecc-sm8650.c:
static const struct clk_parent_data lpass_core_cc_parent_data_ao_2[] = {
	{ .index = 1 },
	{ .hw = &lpass_core_cc_dig_pll.clkr.hw },
	{ .hw = &lpass_core_cc_dig_pll_out_main_div_clk_src.clkr.hw },
};

Because lpass_core only provides index 0, of_parse_clkspec() fails with
-ENOENT for index 1, leaving lpass_core_cc_core_clk_src and its child
branch clocks (such as LPASS_CORE_CC_CORE_CLK and
LPASS_CORE_CC_SYSNOC_MPORT_CORE_CLK) without their safe XO parent when
clk_rcg2_shared_init() parks the RCG.

> +
> +	lpass_hm: clock-controller@7e00000 {
> +		compatible = "qcom,sm8650-lpasshm";
> +		reg = <0 0x07e00000 0 0xa000>;
> +
> +		clocks = <&bi_tcxo_div2>;
> +		clock-names = "bi_tcxo";
> +
> +		power-domains = <&rpmhpd RPMHPD_LCX>;
> +
> +		required-opps = <&rpmhpd_opp_nom>;
> +
> +		#clock-cells = <1>;
> +		#power-domain-cells = <1>;
> +	};
> +
> +	lpass_cpu: audio@7b87000 {
> +		compatible = "qcom,sm8650-lpass-cpu";

[Severity: Low]
This isn't a bug, but audio@7b87000 is placed after
clock-controller@7e00000 instead of in unit-address order, and compatible
is placed after reg in clock-controller@6bc0000 and
clock-controller@6e00000 above.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=21

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

* Re: [PATCH RFC 17/23] ASoC: qcom: add SM8650 LPASS CPU driver
  2026-11-13 18:38 ` [PATCH RFC 17/23] ASoC: qcom: add SM8650 LPASS CPU driver Neil Armstrong
@ 2026-11-13 19:22   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:22 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] ASoC: qcom: wrong lpm_type for lpass-rxtx-lpaif in
  sm8650_lpaif_mi2s_desc[]
- [Medium] ASoC: qcom: missing set_bit() for RX channels in
  sm8650_lpass_alloc_dma_channel()
--

commit ee9ae911e7b58d190ad971326ccfe23116c1a06b
Author: Neil Armstrong <neil.armstrong@linaro.org>

ASoC: qcom: add SM8650 LPASS CPU driver

This commit adds the SM8650 LPASS CPU DAI driver variant supporting MI2S,
DisplayPort, and codec DMA audio interfaces when driven directly by the
application processor.

> diff --git a/sound/soc/qcom/lpass-sm8650.c b/sound/soc/qcom/lpass-sm8650.c
> new file mode 100644
> index 0000000000000..42050f56a4847
> --- /dev/null
> +++ b/sound/soc/qcom/lpass-sm8650.c
> @@ -0,0 +1,651 @@

[ ... ]

> +static int sm8650_lpass_alloc_dma_channel(struct lpass_data *drvdata,
> +					  int direction, unsigned int dai_id)
> +{

[ ... ]

> +	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
> +		chan = find_first_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
> +					   v->rxtx_rdma_channels);
> +		if (chan >= v->rxtx_rdma_channels)
> +			return -EBUSY;
> +		break;
> +	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
> +		chan = find_next_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
> +					  v->rxtx_wrdma_channel_start +
> +					  v->rxtx_wrdma_channels,
> +					  v->rxtx_wrdma_channel_start);
> +		if (chan >= v->rxtx_wrdma_channel_start + v->rxtx_wrdma_channels)
> +			return -EBUSY;
> +		set_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
> +		break;
> +	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
> +		chan = find_next_zero_bit(&drvdata->va_dma_ch_bit_map,
> +					  v->va_wrdma_channel_start +
> +					  v->va_wrdma_channels,
> +					  v->va_wrdma_channel_start);
> +		if (chan >= v->va_wrdma_channel_start + v->va_wrdma_channels)
> +			return -EBUSY;
> +		set_bit(chan, &drvdata->va_dma_ch_bit_map);
> +		break;
> +	case LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9:
> +		chan = find_first_zero_bit(&drvdata->wsa_dma_ch_bit_map,
> +					   v->wsa_rdma_channels);
> +		if (chan >= v->wsa_rdma_channels)
> +			return -EBUSY;
> +		break;

[Severity: Medium]
Should the LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9 and
LPASS_CDC_DMA_WSA_RX0 ... LPASS_CDC_DMA_WSA_RX9 cases call set_bit() on
drvdata->rxtx_dma_ch_bit_map and drvdata->wsa_dma_ch_bit_map before
breaking?

Without set_bit(), channel 0 is never marked as allocated (even though
sm8650_lpass_free_dma_channel() clears the bit on close), so subsequent
allocations from those bitmaps will hand out channel 0 again and overwrite
drvdata->rxtx_substream[0] or drvdata->wsa_substream[0] in
lpass_platform_pcmops_open().

[ ... ]

> +/* MI2S mapping over LPAIF instances */
> +static const struct lpaif_mi2s_desc sm8650_lpaif_mi2s_desc[] = {
> +	{
> +		.reg_name = "lpass-lpaif",
> +		.irq_name = "lpass-irq-lpaif",
> +		.lpm_type = LPASS_LPM_NONE,
> +		.port_desc = (const struct mi2s_port_desc[]) {
> +			{ .intf = 0, .id = MI2S_PRIMARY, },
> +			{ .intf = 1, .id = MI2S_SECONDARY, },
> +		},
> +		.port_desc_count = 2,
> +	},
> +	{
> +		.reg_name = "lpass-rxtx-lpaif",
> +		.irq_name = "lpass-irq-rxtxif",
> +		.lpm_type = LPASS_VA_CDC_LPM,
> +		.port_desc = (const struct mi2s_port_desc[]) {
> +			{ .intf = 0, .id = MI2S_QUATERNARY, },
> +		},
> +		.port_desc_count = 1,
> +	},

[Severity: High]
Should the lpass-rxtx-lpaif descriptor use LPASS_RXTX_CDC_LPM instead of
LPASS_VA_CDC_LPM?

lpass-rxtx-lpaif is in the Audio CC / RXTX domain (clocked by
audio_cc_ext_if1_ibit), so setting LPASS_VA_CDC_LPM maps MI2S_QUATERNARY
DMA buffers from the 12 KiB VA LPM region (va_cdc_dma_lpm_buf) and enables
va_mem0 in lpass_cpu_daiops_startup() instead of using the 64 KiB RXTX LPM
region and audio_cc_codec_mem0..3.  If MI2S_QUATERNARY is enabled
alongside LPASS_CDC_DMA_VA_TX0, lpass_platform_allocate_lpm() also fails
probe with -EINVAL because VA Codec DMA already consumes all 12 KiB of VA
LPM, and if MI2S_QUATERNARY is enabled alongside MI2S_QUINARY without VA
Codec DMA, va_cdc_dma_lpm_remain underflows and assigns an offset past the
end of the 12 KiB VA LPM buffer.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=17

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

* Re: [PATCH RFC 22/23] arm64: dts: qcom: sm8650-valve-deckard: enable audio
  2026-11-13 18:38 ` [PATCH RFC 22/23] arm64: dts: qcom: sm8650-valve-deckard: enable audio Neil Armstrong
@ 2026-11-13 19:23   ` sashiko-bot
  0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-13 19:23 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: linux-sound, devicetree, linux-clk, linux-gpio, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] arm64: dts: qcom: sm8650-valve-deckard: missing VA DMIC1
  route in audio-routing
- [Low] arm64: dts: qcom: sm8650-valve-deckard: unused lpass_rxmacro and
  lpass_txmacro enabled
--

commit 159b85c8d3e0dd02d73b32823f4b0aaab92103dd
Author: Neil Armstrong <neil.armstrong@linaro.org>

arm64: dts: qcom: sm8650-valve-deckard: enable audio

This commit includes sm8650-lpass-cpu.dtsi and configures the SM8650 sound
card, lpass_cpu, and macro nodes to drive the speakers on Senary MI2S and
the DMICs on the VA macro.

> diff --git a/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts b/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
> index a365caa28cee4..723f7911fc547 100644
> --- a/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts
> +++ b/arch/arm64/boot/dts/qcom/sm8650-valve-deckard.dts

[ ... ]

> @@ -160,6 +161,37 @@ framebuffer_mem: framebuffer-region@af400000 {
>  		};
>  	};
>  
> +	sound: sound {
> +		compatible = "qcom,sm8650-sndcard", "qcom,sm8450-sndcard";
> +		model = "VALVE-DECKARD";
> +
> +		audio-routing = "VA DMIC0", "vdd-micb";

[Severity: Medium]
Should audio-routing also include "VA DMIC1", "vdd-micb"?

lpass_vamacro uses dmic01_default and the LPASS_CDC_DMA_VA_TX0 DAI in
sound/soc/qcom/lpass-sm8650.c sets channels_min = 2.  In
sound/soc/codecs/lpass-va-macro.c, "VA DMIC0" and "VA DMIC1" are separate
DAPM ADC widgets, so without routing "VA DMIC1" to "vdd-micb", enabling VA
DMIC1 without VA DMIC0 will not power the vdd-micb regulator supply.

> +
> +		speaker-dai-link {
> +			link-name = "SPKR Playback";
> +
> +			cpu {
> +				sound-dai = <&lpass_cpu MI2S_SENARY>;
> +			};
> +
> +			codec {
> +				sound-dai = <&left_spkr_i2s>, <&right_spkr_i2s>;
> +			};
> +		};
> +
> +		va-dai-link {
> +			link-name = "VA Capture";
> +
> +			cpu {
> +				sound-dai = <&lpass_cpu LPASS_CDC_DMA_VA_TX0>;
> +			};
> +
> +			codec {
> +				sound-dai = <&lpass_vamacro 0>;
> +			};
> +		};
> +	};

[ ... ]

> +&lpass_rxmacro {
> +	status = "okay";
> +};
> +
> +&lpass_txmacro {
> +	status = "okay";
> +};
> +
> +&lpass_vamacro {
> +	pinctrl-0 = <&dmic01_default>;
> +	pinctrl-names = "default";
> +	vdd-micb-supply = <&vreg_l10b_1p8>;
> +	qcom,dmic-sample-rate = <2400000>;
> +};

[Severity: Low]
Are lpass_rxmacro and lpass_txmacro intentionally enabled here?

The sound card only uses MI2S_SENARY and lpass_vamacro, and nothing on this
board consumes DAIs or clocks from lpass_rxmacro or lpass_txmacro.  Probing
them registers unused ASoC components and calls lpass_macro_pds_init(),
which holds active runtime PM references on the macro and dcodec power
domains for the lifetime of the drivers.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-topic-sm8650-el2-v1-0-5183f6aab9fc@linaro.org?part=22

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

end of thread, other threads:[~2026-11-13 19:23 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-13 18:38 [PATCH RFC 00/23] arm64: qcom: enable SM8650 audio without ADSP firmware Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 01/23] dt-bindings: clock: qcom: add SM8650 LPASS core & audio clock controllers Neil Armstrong
2026-11-13 18:57   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 02/23] clk: " Neil Armstrong
2026-11-13 19:14   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 03/23] dt-bindings: pinctrl: qcom,sm8650-lpass-lpi-pinctrl: document clocks and power domaines when ADSP is not used Neil Armstrong
2026-11-13 19:02   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 04/23] ASoC: dt-bindings: qcom,lpass: add Senary & Septenary MI2S ids Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 05/23] ASoC: dt-bindings: qcom,lpass: add WSA RX and TX channels ids Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 06/23] ASoC: dt-bindings: qcom,lpass-*-macro: document less clock when ADSP is not used Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 07/23] ASoC: dt-bindings: qcom,lpass-cpu: document the SM8650 LPASS Neil Armstrong
2026-11-13 18:57   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 08/23] ASoC: qcom: lpass-cpu: get the optional codec_mem3 clock Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 09/23] ASoC: qcom: lpass: add support for optional WSA interface Neil Armstrong
2026-11-13 19:11   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 10/23] ASoC: qcom: lpass-cdc-dma: add support for WSA codec DMA Neil Armstrong
2026-11-13 19:07   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 11/23] ASoC: qcom: lpass: prepare to handle multiple lpaifs for i2s Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 12/23] ASoC: qcom: lpass: add support for MI2S_SENARY & MI2S_SEPTENARY Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 13/23] ASoC: qcom: lpass: support mapping I2S ports to multiple LPAIFs Neil Armstrong
2026-11-13 19:04   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 14/23] ASoC: qcom: lpass: support LPM memory type for I2S LPAIFs Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 15/23] ASoC: qcom: lpass: allocate LPM buffer memory to DAIs Neil Armstrong
2026-11-13 19:12   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 16/23] ASoC: qcom: lpass: enable the right codec_mem clock when usin LPM memory Neil Armstrong
2026-11-13 19:06   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 17/23] ASoC: qcom: add SM8650 LPASS CPU driver Neil Armstrong
2026-11-13 19:22   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 18/23] ASoC: qcom: swd: allow LPASS_CDC_DMA_WSA_RX0 as soundwire dai Neil Armstrong
2026-11-13 18:38 ` [PATCH RFC 19/23] ASoC: qcom: sc8280xp: allow using the sc8280xp card with lpass-cpu Neil Armstrong
2026-11-13 19:12   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 20/23] arm64: dts: qcom: sm8650: move EL1 Gunyah nodes to sm8650-el1.dtsi Neil Armstrong
2026-11-13 19:08   ` Val Packett
2026-11-13 19:11   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 21/23] arm64: dts: qcom: add SM8650 LPASS CPU dtsi Neil Armstrong
2026-11-13 19:18   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 22/23] arm64: dts: qcom: sm8650-valve-deckard: enable audio Neil Armstrong
2026-11-13 19:23   ` sashiko-bot
2026-11-13 18:38 ` [PATCH RFC 23/23] arm64: defconfig: enable the SM8650 LPASS CPU driver Neil Armstrong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox