Devicetree
 help / color / mirror / Atom feed
* [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform
@ 2026-06-05 10:37 Harendra Gautam
  2026-06-05 10:37 ` [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header Harendra Gautam
                   ` (12 more replies)
  0 siblings, 13 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

This series introduces the Qualcomm Audio Interface (QAIF) driver for
the Shikra audio platform.


Hardware IP Overview
====================
QAIF (Qualcomm Audio Interface) is a fully configurable DMA-based audio
subsystem used to transmit and receive audio data over serial audio
interfaces (PCM, TDM, MI2S) and to provide a DMA interface for internal
codec connections. It manages two independent data paths:

  Unified Audio Interface (AIF): Serialises and deserialises PCM audio
  between system memory and external serial audio peripherals. Each
  interface supports up to 8 independent data lanes, each configurable
  as TX (speaker) or RX (mic). All lanes of an interface share a single
  bit clock and frame sync. The AUD_INTFa hardware block controls the
  serial protocol -- sync source (master/slave), sync mode (short/long
  PCM, TDM, MI2S), sync delay and inversion, slot and sample widths,
  active slot masks, lane enable/direction, mono/stereo mode and
  full-cycle path support for long chip-to-chip connections.

  Codec DMA Interface (CIF): Provides a parallel DMA interface to the
  internal Bolero digital codec. The RDDMA path carries playback data
  to Bolero and the WRDMA path carries capture data from Bolero. Each
  channel has an INTF_CFG register controlling the active channel enable
  mask, frame-sync selection, frame-sync delay, frame-sync output gating,
  dynamic clock gating and 16-bit packing/unpacking.

  DMA Engine: Read DMAs (RDDMA) fetch audio from DDR/TCM/LPM into a
  shared on-chip SRAM latency buffer (SHRAM) and drain it to the
  interface. Write DMAs (WRDMA) collect data from the interface into
  SHRAM and write it to memory. Each DMA channel owns a private SHRAM
  region. Two QSB master ports (QXM0 and QXM1) provide the memory
  fabric. Burst sizes of 1/2/4/8/16 beats and up to 4 outstanding
  transactions per DMA are supported.

  Execution Environments (EE): Resources (DMAs, audio interfaces,
  interface groups) are partitioned among up to 5 EEs via map registers.
  Each EE receives an independent interrupt output. The interrupt
  hierarchy has two levels: a summary register identifies the event class
  (DMA period, underflow/overflow, error response, AUD_INTF
  underflow/overflow, group done, rate detector, VFR), and per-resource
  status registers identify the specific channel and event type.

  Interface Grouping (Bonding): Multiple AIF and CIF interfaces can be
  bonded into a group to start synchronously and align their DMA period
  interrupts within half a frame duration using the RDDMA padding feature.

  Rate Detector: Two rate detector blocks measure the frequency of
  incoming frame sync or word select signals and generate interrupts on
  rate change, undetected rate or sync timeout.

Block Diagram
=============

  System Memory (DDR / LPM / TCM)
  +---------------------------------+
  |  Circular Buffers (ping-pong)   |
  +----------+----------+-----------+
             |          ^
       64-bit AXI  64-bit AXI
             |          |
  +----------v----------+-----------+
  |        QSB Master Ports         |
  |  +----------+  +----------+     |
  |  |   QXM0   |  |   QXM1   |     |
  |  +----+-----+  +-----+----+     |
  +-------|--------------|----------+
          |              |
  +-------v--------------v----------+
  |         Shared RAM (SHRAM)       |
  |  +------------+  +------------+ |
  |  | QXM0 Read  |  | QXM0 Write | |
  |  | SHRAM      |  | SHRAM      | |
  |  +------------+  +------------+ |
  |  +------------+  +------------+ |
  |  | QXM1 Read  |  | QXM1 Write | |
  |  | SHRAM      |  | SHRAM      | |
  |  +------------+  +------------+ |
  +---+--------+--------+-------+---+
      |        |        |       |
  +---v--+  +--v---+ +--v---+ +-v----+
  |RDDMA |  |RDDMA | |WRDMA | |WRDMA |
  | AIF  |  | CIF  | | AIF  | | CIF  |
  |[0..n]|  |[0..n]| |[0..n]| |[0..n]|
  +--+---+  +--+---+ +--+---+ +-+----+
     |         |       ^          ^
     | TX      | TX    | RX       | RX
     v         v       |          |
  +--+--------------------+  +----+----------+
  |  Unified Audio Intf   |  | Codec DMA     |
  |  (AIF 0..12)          |  | Interface     |
  |                       |  | (CIF)         |
  |  AUD_INTFa block:     |  |               |
  |  - Serializer (TX)    |  | RDDMA: DDR -> |
  |  - De-serializer (RX) |  |   internal    |
  |  - Sync gen/detect    |  |   codec       |
  |  - Up to 8 data lanes |  | WRDMA: codec  |
  |  - PCM / TDM / MI2S   |  |   -> DDR      |
  |  - Near Pad Logic     |  | Up to 16 ch   |
  +--+--------------------+  +----+----------+
     |  Lane 0..7 (TX/RX)       |  Parallel bus
     |  Bit clk + Frame sync    |  + Frame sync
     v                          v
  +--+--------+          +------+------+
  | External  |          | Internal    |
  | Serial    |          | Digital     |
  | Peripherals|         | Codec       |
  | (PCM/TDM/ |          | (Bolero/    |
  |  MI2S)    |          |  WCD)       |
  +-----------+          +-------------+

Software Design
===============
The driver follows the standard ALSA SoC split:

  qaif-cpu.c: CPU DAI component. Manages clocks, initialises regmap
  bitfield handles for all DMA and interface control registers, implements
  DAI ops (startup/shutdown/hw_free/hw_params/trigger) for both AIF and
  CIF paths, registers an of_xlate_dai_name callback so that sound-dai
  references using non-sequential DAI IDs resolve correctly, and parses
  per-interface TDM/MI2S configuration from DT child nodes.

  qaif-platform.c: PCM platform component. Handles DMA buffer allocation
  (dma_alloc_pages()), PCM ops (open/close/hw_params/prepare/trigger/
  pointer/mmap/copy), two-level IRQ dispatch with period-elapsed
  notification, and component suspend/resume across power collapse.

  qaif-shikra.c: Shikra SoC-specific variant descriptor. Provides all
  register field definitions, DMA-to-DAI index maps, SHRAM geometry,
  clock names and the DAI driver array. This abstraction keeps the core
  driver portable across future QAIF integrations.

  qaif.h / qaif-reg.h: Shared data structures, constants and the complete
  MMIO register address map consumed by both the CPU and platform drivers.

  common.c/h: This series also adds asoc_qcom_of_xlate_dai_name(), a shared
  helper that resolves a sound-dai phandle argument to a DAI name by
  searching the component DAI driver array by ID. Both lpass-cpu.c and
  qaif-cpu.c use thin wrappers around this helper, replacing duplicate
  private implementations.

The series is split by functionality to aid review -- register map and
data structures first, then CIF ops, AIF ops, probe infrastructure, PCM
ops, IRQ handling and finally the Shikra variant glue.

Tested on Shikra with 48 kHz stereo MI2S playback and capture over the
Audio Interface Zero (AIF0) and Bolero CDC DMA RX/TX paths.


Harendra Gautam (13):
  dt-bindings: sound: Add Qualcomm QAIF DAI ID header
  dt-bindings: sound: Add Qualcomm QAIF binding
  MAINTAINERS: Add Qualcomm QAIF driver entry
  ASoC: qcom: Add QAIF hardware register map
  ASoC: qcom: Add QAIF shared data structures and variant interface
  ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops
  ASoC: qcom: Add QAIF AIF DAI ops
  ASoC: qcom: Add generic of_xlate_dai_name helper to common
  ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper
  ASoC: qcom: Add QAIF regmap, DT parsing and platform init
  ASoC: qcom: Add QAIF PCM operations
  ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register
  ASoC: qcom: Add Shikra QAIF support

 .../devicetree/bindings/sound/qcom,qaif.yaml  |  361 ++++
 MAINTAINERS                                   |   10 +
 include/dt-bindings/sound/qcom,qaif.h         |   92 ++
 sound/soc/qcom/Kconfig                        |   11 +
 sound/soc/qcom/Makefile                       |    2 +
 sound/soc/qcom/common.c                       |   34 +
 sound/soc/qcom/common.h                       |    5 +
 sound/soc/qcom/lpass-cpu.c                    |   19 +-
 sound/soc/qcom/qaif-cpu.c                     | 1586 ++++++++++++++++++++
 sound/soc/qcom/qaif-platform.c                | 1169 +++++++++++++++
 sound/soc/qcom/qaif-reg.h                     |  405 +++++
 sound/soc/qcom/qaif-shikra.c                  |  585 ++++++++
 sound/soc/qcom/qaif.h                         |  504 +++++++
 13 files changed, 4769 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,qaif.yaml
 create mode 100644 include/dt-bindings/sound/qcom,qaif.h
 create mode 100644 sound/soc/qcom/qaif-cpu.c
 create mode 100644 sound/soc/qcom/qaif-platform.c
 create mode 100644 sound/soc/qcom/qaif-reg.h
 create mode 100644 sound/soc/qcom/qaif-shikra.c
 create mode 100644 sound/soc/qcom/qaif.h

-- 
2.34.1

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

* [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:42   ` Krzysztof Kozlowski
  2026-06-05 10:37 ` [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding Harendra Gautam
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add a dt-bindings header for the Qualcomm Audio Interface (QAIF) controller
DAI IDs. This provides shared constants for devicetree sound-dai references
and QAIF aif-interface reg values instead of using raw numeric IDs.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 include/dt-bindings/sound/qcom,qaif.h | 92 ++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 include/dt-bindings/sound/qcom,qaif.h

diff --git a/include/dt-bindings/sound/qcom,qaif.h b/include/dt-bindings/sound/qcom,qaif.h
new file mode 100644
index 000000000000..72030838c26b
--- /dev/null
+++ b/include/dt-bindings/sound/qcom,qaif.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * DAI IDs for the Qualcomm Audio Interface (QAIF) controller.
+ * These values are used in devicetree sound-dai references and as
+ * the reg value of aif-interface child nodes.
+ */
+#ifndef __DT_QCOM_QAIF_H
+#define __DT_QCOM_QAIF_H
+
+/* qcom,qaif-aif-sync-mode values */
+#define QAIF_AIF_SYNC_MODE_SHORT	0	/* Short (pulse) sync */
+#define QAIF_AIF_SYNC_MODE_LONG		1	/* Long (level) sync */
+
+/* qcom,qaif-aif-sync-src values */
+#define QAIF_AIF_SYNC_SRC_SLAVE		0	/* Sync slave -- clock from external */
+#define QAIF_AIF_SYNC_SRC_MASTER	1	/* Sync master -- drive clock/frame */
+
+/* qcom,qaif-aif-lane-config enable values */
+#define QAIF_AIF_LANE_DISABLE		0
+#define QAIF_AIF_LANE_ENABLE		1
+
+/* qcom,qaif-aif-lane-config direction values */
+#define QAIF_AIF_LANE_DIR_TX		0	/* TX (playback, speaker) */
+#define QAIF_AIF_LANE_DIR_RX		1	/* RX (capture, mic) */
+
+/*
+ * AIF (Unified Audio Interface) DAI IDs -- AIF0 through AIF12.
+ * Each AIF supports PCM, TDM and MI2S serial protocols over up to
+ * 8 independent data lanes sharing a single bit clock and frame sync.
+ */
+#define QAIF_MI2S_TDM_AIF0	0
+#define QAIF_MI2S_TDM_AIF1	1
+#define QAIF_MI2S_TDM_AIF2	2
+#define QAIF_MI2S_TDM_AIF3	3
+#define QAIF_MI2S_TDM_AIF4	4
+#define QAIF_MI2S_TDM_AIF5	5
+#define QAIF_MI2S_TDM_AIF6	6
+#define QAIF_MI2S_TDM_AIF7	7
+#define QAIF_MI2S_TDM_AIF8	8
+#define QAIF_MI2S_TDM_AIF9	9
+#define QAIF_MI2S_TDM_AIF10	10
+#define QAIF_MI2S_TDM_AIF11	11
+#define QAIF_MI2S_TDM_AIF12	12
+
+/*
+ * CIF (Codec Interface) RX DAI IDs -- playback to internal codec.
+ * RDDMA channels fetch audio from memory and drain it to the codec.
+ */
+#define QAIF_CDC_DMA_RX0	13
+#define QAIF_CDC_DMA_RX1	14
+#define QAIF_CDC_DMA_RX2	15
+#define QAIF_CDC_DMA_RX3	16
+#define QAIF_CDC_DMA_RX4	17
+#define QAIF_CDC_DMA_RX5	18
+#define QAIF_CDC_DMA_RX6	19
+#define QAIF_CDC_DMA_RX7	20
+#define QAIF_CDC_DMA_RX8	21
+#define QAIF_CDC_DMA_RX9	22
+
+/*
+ * CIF (Codec Interface) TX DAI IDs -- capture from internal codec.
+ * WRDMA channels collect audio from the codec and write it to memory.
+ */
+#define QAIF_CDC_DMA_TX0	23
+#define QAIF_CDC_DMA_TX1	24
+#define QAIF_CDC_DMA_TX2	25
+#define QAIF_CDC_DMA_TX3	26
+#define QAIF_CDC_DMA_TX4	27
+#define QAIF_CDC_DMA_TX5	28
+#define QAIF_CDC_DMA_TX6	29
+#define QAIF_CDC_DMA_TX7	30
+#define QAIF_CDC_DMA_TX8	31
+#define QAIF_CDC_DMA_TX9	32
+
+/*
+ * CIF (Codec Interface) VA TX DAI IDs -- capture from voice activity codec.
+ * WRDMA channels collect audio from the VA codec and write it to memory.
+ */
+#define QAIF_CDC_DMA_VA_TX0	33
+#define QAIF_CDC_DMA_VA_TX1	34
+#define QAIF_CDC_DMA_VA_TX2	35
+#define QAIF_CDC_DMA_VA_TX3	36
+#define QAIF_CDC_DMA_VA_TX4	37
+#define QAIF_CDC_DMA_VA_TX5	38
+#define QAIF_CDC_DMA_VA_TX6	39
+#define QAIF_CDC_DMA_VA_TX7	40
+#define QAIF_CDC_DMA_VA_TX8	41
+#define QAIF_CDC_DMA_VA_TX9	42
+
+#endif /* __DT_QCOM_QAIF_H */
--
2.34.1

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

* [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
  2026-06-05 10:37 ` [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:46   ` Krzysztof Kozlowski
                     ` (2 more replies)
  2026-06-05 10:37 ` [PATCH 3/13] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
                   ` (10 subsequent siblings)
  12 siblings, 3 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add a Devicetree binding for the Qualcomm Audio Interface (QAIF) CPU DAI
controller used on the Shikra audio platform.

QAIF moves PCM data between system memory and external serial audio
interfaces through the AIF path, and between memory and the internal Bolero
digital codec through the CIF path. The controller needs a binding so
platform Devicetree files can describe its MMIO region, DMA IOMMU stream,
clocks, interrupt, DAI cells and per-interface AIF configuration.

Describe the single register region, one EE interrupt, the required GCC
LPASS and audio core clocks, the DMA IOMMU mapping, and 'aif-interface@N'
child nodes used for static PCM, TDM or MI2S configuration.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 .../devicetree/bindings/sound/qcom,qaif.yaml  | 353 ++++++++++++++++++
 1 file changed, 353 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,qaif.yaml

diff --git a/Documentation/devicetree/bindings/sound/qcom,qaif.yaml b/Documentation/devicetree/bindings/sound/qcom,qaif.yaml
new file mode 100644
index 000000000000..5b385e05a650
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,qaif.yaml
@@ -0,0 +1,361 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,qaif.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Audio Interface (QAIF) CPU DAI Controller
+
+maintainers:
+  - Harendra Gautam <harendra.gautam@oss.qualcomm.com>
+
+description:
+  |
+  The Qualcomm Audio Interface (QAIF) is a fully configurable DMA-based
+  audio subsystem controller. It serialises and deserialises PCM audio
+  between system memory and external serial audio peripherals (PCM, TDM,
+  I2S, MI2S) through the AIF path, and transfers parallel audio between
+  memory and an internal WCD codec through the CIF path.
+
+  AIF (Audio Interface): up to 13 multi-lane Unified Audio Interfaces,
+  each supporting up to 8 independent data lanes. Each lane is individually
+  configurable as TX (output/speaker) or RX (input/mic). All lanes of an
+  interface share a single bit clock and frame sync. Supported modes are
+  PCM (short/long sync), TDM, and MI2S (stereo/mono). Per-interface
+  configuration includes sync source (master/slave), sync mode, sync delay,
+  sync inversion, slot width (8/16/24/32-bit), sample width, active slot
+  masks (up to 32 slots), bits-per-lane frame size, lane enable/direction
+  masks, loopback, output-enable control, and full-cycle path support for
+  long chip-to-chip connections.
+
+  CIF (Codec Interface): up to 32 RDDMA (playback) and 32 WRDMA (capture)
+  channels connecting to an internal codec over a parallel bus. Each channel
+  supports active-channel enable mask (up to 16 channels), frame-sync
+  selection, frame-sync delay, frame-sync output gating, dynamic clock
+  gating, and 16-bit packing/unpacking.
+
+  Note on RX/TX naming convention: in QAIF, RX refers to the capture path
+  (audio received from the interface into memory) and TX refers to the
+  playback path (audio transmitted from memory to the interface). This
+  applies to both AIF lane directions and CIF slot/mask properties.
+
+  DMA engine: RDDMA fetches audio from DDR/TCM/LPM into a shared SRAM
+  latency buffer (SHRAM) and drains it to the interface. WRDMA collects
+  data from the interface into SHRAM and writes it to memory. Each DMA
+  owns a private SHRAM region defined by start address and length registers.
+  Burst sizes of 1/2/4/8/16 beats (64-bit) are supported with up to 4
+  outstanding transactions per DMA. Two QSB master ports (QXM0 for TCM,
+  QXM1 for DDR/LPM) provide the memory interface.
+
+  Resources are partitioned among up to 5 Execution Engines (EEs) via
+  EE map registers. Each EE owns a set of DMAs, audio interfaces, and
+  interface groups, and receives its own independent interrupt output.
+  The interrupt hierarchy has a two-level structure: a summary register
+  identifies the event class (DMA period, underflow/overflow, error
+  response, audio interface underflow/overflow, group done, rate detector,
+  VFR), and per-resource status registers identify the specific channel.
+
+  Interface grouping (bonding) allows up to 6 groups of audio and codec
+  interfaces to start synchronously and align their DMA period interrupts
+  within half a frame duration using the RDDMA padding feature.
+
+  Two rate detector blocks measure the frequency of incoming frame sync or
+  word select signals and generate interrupts on rate change, undetected
+  rate, or sync timeout.
+
+  Block diagram::
+
+    System Memory (DDR / LPM / TCM)
+    +---------------------------------+
+    |  Circular Buffers (ping-pong)   |
+    +----------+----------+-----------+
+               |          ^
+         64-bit AXI  64-bit AXI
+               |          |
+    +----------v----------+-----------+
+    |        QSB Master Ports         |
+    |  +----------+  +----------+     |
+    |  |   QXM0   |  |   QXM1   |     |
+    |  +----+-----+  +-----+----+     |
+    +-------|--------------|----------+
+            |              |
+    +-------v--------------v----------+
+    |         Shared RAM (SHRAM)       |
+    |  +------------+  +------------+ |
+    |  | QXM0 Read  |  | QXM0 Write | |
+    |  | SHRAM      |  | SHRAM      | |
+    |  +------------+  +------------+ |
+    |  +------------+  +------------+ |
+    |  | QXM1 Read  |  | QXM1 Write | |
+    |  | SHRAM      |  | SHRAM      | |
+    |  +------------+  +------------+ |
+    +---+--------+--------+-------+---+
+        |        |        |       |
+    +---v--+  +--v---+ +--v---+ +-v----+
+    |RDDMA |  |RDDMA | |WRDMA | |WRDMA |
+    | AIF  |  | CIF  | | AIF  | | CIF  |
+    |[0..n]|  |[0..n]| |[0..n]| |[0..n]|
+    +--+---+  +--+---+ +--+---+ +-+----+
+       |         |       ^          ^
+       | TX      | TX    | RX       | RX
+       v         v       |          |
+    +--+--------------------+  +----+----------+
+    |  Unified Audio Intf   |  | Codec DMA     |
+    |  (AIF 0..12)          |  | Interface     |
+    |                       |  | (CIF)         |
+    |  AUD_INTFa block:     |  |               |
+    |  - Serializer (TX)    |  | RDDMA: DDR -> |
+    |  - De-serializer (RX) |  |   internal    |
+    |  - Sync gen/detect    |  |   codec       |
+    |  - Up to 8 data lanes |  | WRDMA: codec  |
+    |  - PCM / TDM / MI2S   |  |   -> DDR      |
+    |  - Near Pad Logic     |  | Up to 16 ch   |
+    +--+--------------------+  +----+----------+
+       |  Lane 0..7 (TX/RX)       |  Parallel bus
+       |  Bit clk + Frame sync    |  + Frame sync
+       v                          v
+    +--+--------+          +------+------+
+    | External  |          | Internal    |
+    | Serial    |          | Digital     |
+    | Peripherals|         | Codec       |
+    | (PCM/TDM/ |          | (Bolero/    |
+    |  MI2S)    |          |  WCD)       |
+    +-----------+          +-------------+
+
+properties:
+  compatible:
+    enum:
+      - qcom,shikra-qaif-cpu
+
+  reg:
+    maxItems: 1
+
+  iommus:
+    maxItems: 1
+
+  clocks:
+    minItems: 15
+    maxItems: 15
+
+  clock-names:
+    items:
+      - const: lpass_config_clk
+      - const: lpass_core_axim_clk
+      - const: aud_dma_clk
+      - const: aud_dma_mem_clk
+      - const: bus_clk
+      - const: aif_if0_ebit_clk
+      - const: aif_if0_ibit_clk
+      - const: aif_if1_ebit_clk
+      - const: aif_if1_ibit_clk
+      - const: aif_if2_ebit_clk
+      - const: aif_if2_ibit_clk
+      - const: aif_if3_ebit_clk
+      - const: aif_if3_ibit_clk
+      - const: ext_mclka_clk
+      - const: ext_mclkb_clk
+
+  interrupts:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  status: true
+
+patternProperties:
+  "^aif-interface@[0-9a-f]+$":
+    type: object
+    description:
+      AIF interface configuration child node. The compatible string
+      identifies the serial protocol the interface is wired for on the
+      board. The unit address matches the hardware AIF interface index.
+    properties:
+      compatible:
+        enum:
+          - qcom,qaif-pcm-dai
+          - qcom,qaif-tdm-dai
+          - qcom,qaif-mi2s-dai
+      reg:
+        maxItems: 1
+        description: |
+          Hardware AIF interface index (AUD_INTFa block index). This value
+          also serves as the ALSA DAI ID; it corresponds directly to the
+          QAIF_MI2S_TDM_AIFn constants in <dt-bindings/sound/qcom,qaif.h>
+          (e.g. reg = <2> selects QAIF_MI2S_TDM_AIF2).
+      qcom,qaif-aif-sync-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Sync mode. Use QAIF_AIF_SYNC_MODE_SHORT (0) for short (pulse)
+          sync or QAIF_AIF_SYNC_MODE_LONG (1) for long (level) sync.
+      qcom,qaif-aif-sync-src:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Sync source. Use QAIF_AIF_SYNC_SRC_SLAVE (0) for slave mode
+          or QAIF_AIF_SYNC_SRC_MASTER (1) for master mode.
+      qcom,qaif-aif-invert-sync:
+        type: boolean
+        description: Invert the frame sync polarity.
+      qcom,qaif-aif-sync-delay:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description: Number of bit-clock cycles to delay the data relative to sync.
+      qcom,qaif-aif-slot-width-rx:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          RX slot width in bits. This is a board-specific hardware constraint
+          determined by the wiring of the serial audio interface.
+      qcom,qaif-aif-slot-width-tx:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          TX slot width in bits. This is a board-specific hardware constraint
+          determined by the wiring of the serial audio interface.
+      qcom,qaif-aif-slot-en-rx-mask:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Bitmask of active RX slots. Board-specific — determined by which
+          TDM slots the codec is wired to on this board.
+      qcom,qaif-aif-slot-en-tx-mask:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Bitmask of active TX slots. Board-specific — determined by which
+          TDM slots the codec is wired to on this board.
+      qcom,qaif-aif-loopback:
+        type: boolean
+        description: Enable loopback mode (presence enables loopback).
+      qcom,qaif-aif-ctrl-data-oe:
+        type: boolean
+        description: Enable output drive on the control/data line.
+      qcom,qaif-aif-lane-config:
+        $ref: /schemas/types.yaml#/definitions/uint32-matrix
+        description:
+          Lane configuration matrix. Each row is a pair <enable direction>
+          for one lane starting from lane 0, up to 8 lanes. Use
+          QAIF_AIF_LANE_ENABLE (1) or QAIF_AIF_LANE_DISABLE (0) for enable.
+          Use QAIF_AIF_LANE_DIR_TX (0) for TX (speaker) or QAIF_AIF_LANE_DIR_RX
+          (1) for RX (mic). TX and RX lanes should each be grouped contiguously.
+        maxItems: 8
+        items:
+          items:
+            - description: Lane enable (0 = disabled, 1 = enabled)
+              enum: [0, 1]
+            - description: Lane direction (0 = TX/speaker, 1 = RX/mic)
+              enum: [0, 1]
+      qcom,qaif-aif-full-cycle-en:
+        type: boolean
+        description: Enable full-cycle sync (effective in sync master mode).
+      qcom,qaif-aif-bits-per-lane:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Number of slots per lane. The frame length is computed as
+          slot-width multiplied by bits-per-lane.
+    if:
+      properties:
+        compatible:
+          const: qcom,qaif-mi2s-dai
+    then:
+      description:
+        MI2S interface. Sync mode and slot-enable masks are fixed by the
+        MI2S protocol and must not be set in DT. Mono/stereo mode is
+        determined at runtime from the stream channel count.
+      properties:
+        qcom,qaif-aif-sync-mode: false
+        qcom,qaif-aif-slot-en-rx-mask: false
+        qcom,qaif-aif-slot-en-tx-mask: false
+    else:
+      description:
+        PCM or TDM interface. Sync mode and slot-enable masks are
+        board-specific and must be provided. Mono mode does not apply.
+      required:
+        - qcom,qaif-aif-sync-mode
+        - qcom,qaif-aif-slot-en-rx-mask
+        - qcom,qaif-aif-slot-en-tx-mask
+
+    required:
+      - compatible
+      - reg
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - iommus
+  - clocks
+  - clock-names
+  - interrupts
+  - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    /* Shikra platform example */
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/sound/qcom,qaif.h>
+    #include <dt-bindings/clock/qcom,shikra-audiocorecc.h>
+    #include <dt-bindings/clock/qcom,gcc-shikra.h>
+
+    qaif_cpu: audio@a000000 {
+        compatible = "qcom,shikra-qaif-cpu";
+        reg = <0x0 0x0a000000 0x0 0x20000>;
+        iommus = <&apps_smmu 0x1c0 0x0>;
+        clocks = <&gcc GCC_LPASS_CONFIG_CLK>,
+                 <&gcc GCC_LPASS_CORE_AXIM_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AUD_DMA_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AUD_DMA_MEM_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_BUS_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF0_EBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF0_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF1_EBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF1_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF2_EBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF2_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF3_EBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF3_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_EXT_MCLKA_OUT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_EXT_MCLKB_OUT_CLK>;
+        clock-names = "lpass_config_clk",
+                      "lpass_core_axim_clk",
+                      "aud_dma_clk",
+                      "aud_dma_mem_clk",
+                      "bus_clk",
+                      "aif_if0_ebit_clk",
+                      "aif_if0_ibit_clk",
+                      "aif_if1_ebit_clk",
+                      "aif_if1_ibit_clk",
+                      "aif_if2_ebit_clk",
+                      "aif_if2_ibit_clk",
+                      "aif_if3_ebit_clk",
+                      "aif_if3_ibit_clk",
+                      "ext_mclka_clk",
+                      "ext_mclkb_clk";
+        #sound-dai-cells = <1>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        interrupts = <GIC_SPI 331 IRQ_TYPE_LEVEL_HIGH>;
+        status = "okay";
+
+        qaif_aif_if2: aif-interface@2 {
+            compatible = "qcom,qaif-tdm-dai";
+            reg = <QAIF_MI2S_TDM_AIF2>;
+            qcom,qaif-aif-sync-mode = <QAIF_AIF_SYNC_MODE_LONG>;
+            qcom,qaif-aif-sync-src = <QAIF_AIF_SYNC_SRC_MASTER>;
+            qcom,qaif-aif-sync-delay = <1>;
+            qcom,qaif-aif-slot-width-rx = <32>;
+            qcom,qaif-aif-slot-width-tx = <32>;
+            qcom,qaif-aif-slot-en-rx-mask = <0x3>;
+            qcom,qaif-aif-slot-en-tx-mask = <0x3>;
+            qcom,qaif-aif-ctrl-data-oe;
+            /* Lane 0: RX (mic); Lane 1: TX (speaker) */
+            qcom,qaif-aif-lane-config = <QAIF_AIF_LANE_ENABLE QAIF_AIF_LANE_DIR_RX>,
+                                        <QAIF_AIF_LANE_ENABLE QAIF_AIF_LANE_DIR_TX>;
+            /* frame length = slot-width (32) * bits-per-lane (2) = 64 bits */
+            qcom,qaif-aif-bits-per-lane = <2>;
+        };
+    };
-- 
2.34.1


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

* [PATCH 3/13] MAINTAINERS: Add Qualcomm QAIF driver entry
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
  2026-06-05 10:37 ` [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header Harendra Gautam
  2026-06-05 10:37 ` [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:52   ` Krzysztof Kozlowski
  2026-06-05 10:37 ` [PATCH 4/13] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add MAINTAINERS coverage for the Qualcomm Audio Interface (QAIF) driver
so changes to its devicetree binding, CPU DAI driver, and PCM platform
driver are routed to the Qualcomm ASoC maintainers and lists.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f877e5aaf2c7..b38f03680ef3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21707,6 +21707,16 @@ F:	sound/soc/codecs/wcd93*.*
 F:	sound/soc/codecs/wsa88*.*
 F:	sound/soc/qcom/
 
+QCOM AUDIO INTERFACE (QAIF) DRIVER
+M:	Harendra Gautam <harendra.gautam@oss.qualcomm.com>
+M:	Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
+L:	linux-sound@vger.kernel.org
+L:	linux-arm-msm@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/sound/qcom,qaif.yaml
+F:	include/dt-bindings/sound/qcom,qaif.h
+F:	sound/soc/qcom/qaif*
+
 QCOM EMBEDDED USB DEBUGGER (EUD)
 M:	Souradeep Chowdhury <quic_schowdhu@quicinc.com>
 L:	linux-arm-msm@vger.kernel.org
--
2.34.1

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

* [PATCH 4/13] ASoC: qcom: Add QAIF hardware register map
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (2 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 3/13] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:37 ` [PATCH 5/13] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add qaif-reg.h with the MMIO register definitions for the Qualcomm Audio
Interface (QAIF) hardware block.

The QAIF driver needs these definitions to program the core registers,
audio interfaces, AIF/CIF DMA channels, interrupt registers, SHRAM/QXM
routing, and SID maps. Also add common accessor macros so the driver can
select the appropriate AIF or CIF DMA register set based on the DAI ID.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif-reg.h | 405 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 405 insertions(+)
 create mode 100644 sound/soc/qcom/qaif-reg.h

diff --git a/sound/soc/qcom/qaif-reg.h b/sound/soc/qcom/qaif-reg.h
new file mode 100644
index 000000000000..1034b50b431a
--- /dev/null
+++ b/sound/soc/qcom/qaif-reg.h
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * qaif-reg.h -- ALSA SoC CPU-Platform DAI driver register header file for QTi QAIF
+ */
+#ifndef __QAIF_REG_H__
+#define __QAIF_REG_H__
+
+#include "qaif.h"
+
+#define QAIF_SUMMARY_IRQSTAT_REG(v)					(0x19188 + (0x1000 * (v->ee)))
+
+
+/* Core HW info */
+#define QAIF_HW_VERSION_REG                                 (0x0000)
+#define QAIF_HW_INFO_REG                                    (0x0004)
+#define QAIF_HW_INFO2_REG                                   (0x0008)
+
+/* Interface lane and channel info */
+#define QAIF_AUD_INTF_LANE_INFO_REG                         (0x0020)
+#define QAIF_AUD_INTF_LANE_INFO2_REG                        (0x0024)
+#define QAIF_CODEC_TX_INTF_CH_INFO_REG(n)                   (0x0028 + (0x4 * (n)))
+#define QAIF_CODEC_RX_INTF_CH_INFO_REG(n)                   (0x0068 + (0x4 * (n)))
+#define QAIF_QXM1_SHRAM_LENGTH_INFO_REG                     (0x0088)
+#define QAIF_QXM0_SHRAM_LENGTH_INFO_REG                     (0x008C)
+#define QAIF_NUM_AUD_INTF_TO_RAIL_INFO_REG                  (0x0090)
+
+/* Debug/control and status */
+#define QAIF_DEBUG_CTL_REG                                  (0x0200)
+#define QAIF_WRDMA_LOOPBACK_EN_REG                          (0x0204)
+#define QAIF_WRDMA_LOOPBACK_SEL_REG                         (0x0208)
+#define QAIF_SHRAM_DYNAMIC_CLK_GATING_EN_REG                (0x0300)
+#define QAIF_AXI_STATUS_REG                                 (0x0304)
+#define QAIF_QSB_DYNAMIC_CLK_GATING_EN_REG                  (0x0308)
+#define QAIF_START_STOP_CTRL_BYPASS_EN_REG                  (0x030C)
+#define QAIF_QXM0_AXI_ATTR_CFG_REG                          (0x040C)
+
+/* QXM request/grant debug */
+#define QAIF_QXM0_AUD_WR_REQ_GNT_DBG_STAT_REG               (0x0500)
+#define QAIF_QXM1_AUD_WR_REQ_GNT_DBG_STAT_REG               (0x0504)
+#define QAIF_QXM0_CODEC_RX_WR_REQ_DBG_STAT_REG              (0x0508)
+#define QAIF_QXM0_CODEC_RX_WR_GNT_DBG_STAT_REG              (0x050C)
+#define QAIF_QXM1_CODEC_RX_WR_REQ_DBG_STAT_REG              (0x0510)
+#define QAIF_QXM1_CODEC_RX_WR_GNT_DBG_STAT_REG              (0x0514)
+#define QAIF_QXM0_AUD_RD_REQ_GNT_DBG_STAT_REG               (0x0518)
+#define QAIF_QXM1_AUD_RD_REQ_GNT_DBG_STAT_REG               (0x051C)
+#define QAIF_QXM0_CODEC_TX_RD_REQ_DBG_STAT_REG              (0x0520)
+#define QAIF_QXM0_CODEC_TX_RD_GNT_DBG_STAT_REG              (0x0524)
+#define QAIF_QXM1_CODEC_TX_RD_REQ_DBG_STAT_REG              (0x0528)
+#define QAIF_QXM1_CODEC_TX_RD_GNT_DBG_STAT_REG              (0x052C)
+#define QAIF_QXM0_EXT_RDDMA_RD_REQ_GNT_DBG_STAT_REG         (0x0530)
+#define QAIF_QXM1_EXT_RDDMA_RD_REQ_GNT_DBG_STAT_REG         (0x0534)
+
+/* QSB transaction debug */
+#define QAIF_QSB_AUD_WR_TXN_DBG_STAT_REG                    (0x0538)
+#define QAIF_QSB_CODEC_RX_WR_TXN_ERR_DBG_STAT_REG           (0x053C)
+#define QAIF_QSB_CODEC_RX_WR_TXN_OKAY_DBG_STAT_REG          (0x0540)
+#define QAIF_QSB_AUD_ADDR_SENT_DBG_STAT_REG                 (0x0544)
+#define QAIF_QSB_CODEC_TX_RD_ADDR_SENT_DBG_STAT_REG         (0x0548)
+#define QAIF_QSB_EXT_RDDMA_RD_ADDR_SENT_DBG_STAT_REG        (0x054C)
+#define QAIF_QSB_CODEC_RX_WR_ADDR_SENT_DBG_STAT_REG         (0x0550)
+#define QAIF_QSB_AUD_RD_TXN_DBG_STAT_REG                    (0x0554)
+#define QAIF_QSB_CODEC_TX_RD_TXN_ERR_DBG_STAT_REG           (0x0558)
+#define QAIF_QSB_CODEC_TX_RD_TXN_RCVD_DBG_STAT_REG          (0x055C)
+#define QAIF_QSB_EXT_RDDMA_RD_TXN_DBG_STAT_REG              (0x0560)
+#define QAIF_QSB_MISC_DBG_STATUS_REG                        (0x0564)
+
+/* Global spare and HWE */
+#define QAIF_GLOBAL_SPARE_IN_REG                            (0x0B00)
+#define QAIF_GLOBAL_SPARE_OUT_REG                           (0x0B04)
+#define QAIF_HWE_CFG_REG                                    (0x0B08)
+
+/* SID maps */
+#define QAIF_WRDMA_SID_MAP_REG                              (0x1B00)
+#define QAIF_CODEC_WRDMA_SID_MAP_REG                        (0x1B40)
+#define QAIF_RDDMA_SID_MAP_REG                              (0x1C00)
+#define QAIF_CODEC_RDDMA_SID_MAP_REG                        (0x1C40)
+
+/* EE overlap interrupts */
+#define QAIF_EE_OVERLAP_IRQ_EN_REG                          (0x1D00)
+#define QAIF_EE_OVERLAP_IRQ_RAW_STATUS_REG                  (0x1D04)
+#define QAIF_EE_OVERLAP_IRQ_CLEAR_REG                       (0x1D08)
+#define QAIF_EE_OVERLAP_IRQ_FORCE_REG                       (0x1D0C)
+
+/* EE assignments and maps */
+#define QAIF_EE_RDDMA_ASSIGNMENT_REG(v)                     (0x19148 + (0x1000 * (v->ee)))
+#define QAIF_EE_WRDMA_ASSIGNMENT_REG(v)                     (0x19150 + (0x1000 * (v->ee)))
+#define QAIF_EE_INTF_ASSIGNMENT_REG(v)                      (0x19158 + (0x1000 * (v->ee)))
+#define QAIF_EE_CODEC_RDDMA_ASSIGNMENT_REG(v)               (0x19308 + (0x1000 * (v->ee)))
+#define QAIF_EE_CODEC_WRDMA_ASSIGNMENT_REG(v)               (0x19318 + (0x1000 * (v->ee)))
+#define QAIF_EE_RDDMA_MAP_REG(v)                            (0x1920 + (0x1000 * (v->ee)))
+#define QAIF_EE_WRDMA_MAP_REG(v)                            (0x1940 + (0x1000 * (v->ee)))
+#define QAIF_EE_INTF_MAP_REG(v)                             (0x1960 + (0x1000 * (v->ee)))
+#define QAIF_EE_CODEC_RDDMA_MAP_REG(v)                      (0x1980 + (0x1000 * (v->ee)))
+#define QAIF_EE_CODEC_WRDMA_MAP_REG(v)                      (0x1A00 + (0x1000 * (v->ee)))
+
+/* EE rate-detection and VFR interrupts */
+#define QAIF_EE_RATE_DET_IRQ_EN_REG(v)                      (0x190F0 + (0x1000 * (v->ee)))
+#define QAIF_EE_RATE_DET_IRQ_STATUS_REG(v)                  (0x190F4 + (0x1000 * (v->ee)))
+#define QAIF_EE_RATE_DET_IRQ_RAW_STATUS_REG(v)              (0x190F8 + (0x1000 * (v->ee)))
+#define QAIF_EE_RATE_DET_IRQ_CLEAR_REG(v)                   (0x190FC + (0x1000 * (v->ee)))
+#define QAIF_EE_RATE_DET_IRQ_FORCE_REG(v)                   (0x19100 + (0x1000 * (v->ee)))
+
+#define QAIF_EE_VFR_IRQ_EN_REG(v)                           (0x19104 + (0x1000 * (v->ee)))
+#define QAIF_EE_VFR_IRQ_STATUS_REG(v)                       (0x19108 + (0x1000 * (v->ee)))
+#define QAIF_EE_VFR_IRQ_RAW_STATUS_REG(v)                   (0x1910C + (0x1000 * (v->ee)))
+#define QAIF_EE_VFR_IRQ_CLEAR_REG(v)                        (0x19110 + (0x1000 * (v->ee)))
+#define QAIF_EE_VFR_IRQ_FORCE_REG(v)                        (0x19114 + (0x1000 * (v->ee)))
+
+/* EE AUD_INTF underflow/overflow interrupts */
+#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_EN_REG(v)            (0x19160 + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_STATUS_REG(v)        (0x19164 + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_RAW_STATUS_REG(v)    (0x19168 + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_CLEAR_REG(v)         (0x1916C + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_FORCE_REG(v)         (0x19170 + (0x1000 * (v->ee)))
+
+#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_EN_REG(v)             (0x19174 + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_STATUS_REG(v)         (0x19178 + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_RAW_STATUS_REG(v)     (0x1917C + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_CLEAR_REG(v)          (0x19180 + (0x1000 * (v->ee)))
+#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_FORCE_REG(v)          (0x19184 + (0x1000 * (v->ee)))
+
+/* EE L2 Period IRQ mux selection */
+#define QAIF_EE_L2_PERIOD_IRQ_0_3_MUX_SEL_REG(v)            (0x19F00 + (0x1000 * (v->ee)))
+#define QAIF_EE_L2_PERIOD_IRQ_4_7_MUX_SEL_REG(v)            (0x19F04 + (0x1000 * (v->ee)))
+
+/* AUD_INTF block (per interface, stride 0x1000 starting at 0x4000) */
+#define QAIF_AUD_INTF_REG_ADDR(offset, intf)                (0x4000 + (offset) + (0x1000 * (intf)))
+
+#define QAIF_AUD_INTF_CTL_REG(intf)                          QAIF_AUD_INTF_REG_ADDR(0x0000, (intf))
+#define QAIF_AUD_INTF_SYNC_CFG_REG(intf)                     QAIF_AUD_INTF_REG_ADDR(0x0004, (intf))
+#define QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(intf)                QAIF_AUD_INTF_REG_ADDR(0x0008, (intf))
+#define QAIF_AUD_INTF_FRAME_CFG_REG(intf)                    QAIF_AUD_INTF_REG_ADDR(0x000C, (intf))
+#define QAIF_AUD_INTF_ACTV_SLOT_EN_TX_REG(intf)              QAIF_AUD_INTF_REG_ADDR(0x0010, (intf))
+#define QAIF_AUD_INTF_ACTV_SLOT_EN_RX_REG(intf)              QAIF_AUD_INTF_REG_ADDR(0x0030, (intf))
+#define QAIF_AUD_INTF_LANE_CFG_REG(intf)                     QAIF_AUD_INTF_REG_ADDR(0x0050, (intf))
+#define QAIF_AUD_INTF_MI2S_CFG_REG(intf)                     QAIF_AUD_INTF_REG_ADDR(0x0054, (intf))
+#define QAIF_AUD_INTF_CFG_REG(intf)                          QAIF_AUD_INTF_REG_ADDR(0x0058, (intf))
+#define QAIF_AUD_INTF_CHAR_CTL_REG(intf)                     QAIF_AUD_INTF_REG_ADDR(0x005C, (intf))
+#define QAIF_AUD_INTF_CHAR_CFG_REG(intf)                     QAIF_AUD_INTF_REG_ADDR(0x0060, (intf))
+#define QAIF_AUD_INTF_CHAR_DATA_REG(intf)                    QAIF_AUD_INTF_REG_ADDR(0x0064, (intf))
+#define QAIF_AUD_INTF_CHAR_DATA_EXT_REG(intf)                QAIF_AUD_INTF_REG_ADDR(0x0068, (intf))
+#define QAIF_AUD_INTF_CHAR_SYNC_REG(intf)                    QAIF_AUD_INTF_REG_ADDR(0x006C, (intf))
+#define QAIF_AUD_INTF_INIT_DBG_STATUS_REG(intf)              QAIF_AUD_INTF_REG_ADDR(0x0FF0, (intf))
+#define QAIF_AUD_INTF_TX_DBG_STATUS_REG(intf)                QAIF_AUD_INTF_REG_ADDR(0x0FF4, (intf))
+#define QAIF_AUD_INTF_RX_DBG_STATUS_REG(intf)                QAIF_AUD_INTF_REG_ADDR(0x0FF8, (intf))
+
+/* RATE_DET block (per detector, stride 0x1000 starting at 0x1E000) */
+#define QAIF_RATE_DET_REG_ADDR(offset, det)                  (0x1E000 + (offset) + (0x1000 * (det)))
+
+#define QAIF_RATE_DET_CONFIG_REG(det)                        QAIF_RATE_DET_REG_ADDR(0x0000, (det))
+#define QAIF_RATE_DET_TARGET1_CONFIG_REG(det)                QAIF_RATE_DET_REG_ADDR(0x0004, (det))
+#define QAIF_RATE_DET_TARGET2_CONFIG_REG(det)                QAIF_RATE_DET_REG_ADDR(0x0008, (det))
+#define QAIF_RATE_DET_BIN_REG(det)                           QAIF_RATE_DET_REG_ADDR(0x000C, (det))
+#define QAIF_RATE_DET_STC_DIFF_REG(det)                      QAIF_RATE_DET_REG_ADDR(0x0010, (det))
+#define QAIF_RATE_DET_SEL_REG(det)                           QAIF_RATE_DET_REG_ADDR(0x0014, (det))
+#define QAIF_RATE_DET_TIMEOUT_CFG_REG(det)                   QAIF_RATE_DET_REG_ADDR(0x0018, (det))
+
+#define QAIF_WRDMA_MAP_QXM									(0X1000)
+#define QAIF_CODEC_WRDMA_MAP_QXM							(0X1004)
+#define QAIF_RDDMA_MAP_QXM									(0X1010)
+#define QAIF_CODEC_RDDMA_MAP_QXM							(0X1014)
+#define QAIF_RDDMA_QXM1_SHRAM_ST_ADDR(i)					(0X1100 + (0x4 * (i)))
+#define QAIF_CODEC_RDDMA_QXM1_SHRAM_ST_ADDR(i)				(0X1140 + (0x4 * (i)))
+#define QAIF_RDDMA_QXM0_SHRAM_ST_ADDR(i)					(0X1200 + (0x4 * (i)))
+#define QAIF_CODEC_RDDMA_QXM0_SHRAM_ST_ADDR(i)				(0X1240 + (0x4 * (i)))
+#define QAIF_RDDMA_QXM1_SHRAM_LEN(i)						(0x1300 + (0x4 * (i)))
+#define QAIF_CODEC_RDDMA_QXM1_SHRAM_LEN(i)					(0x1340 + (0x4 * (i)))
+#define QAIF_RDDMA_QXM0_SHRAM_LEN(i)						(0x1400 + (0x4 * (i)))
+#define QAIF_CODEC_RDDMA_QXM0_SHRAM_LEN(i)					(0x1440 + (0x4 * (i)))
+#define QAIF_WRDMA_QXM1_SHRAM_ST_ADDR(i)					(0x1500 + (0x4 * (i)))
+#define QAIF_CODEC_WRDMA_QXM1_SHRAM_ST_ADDR(i)				(0x1540 + (0x4 * (i)))
+#define QAIF_WRDMA_QXM0_SHRAM_ST_ADDR(i)					(0x1600 + (0x4 * (i)))
+#define QAIF_CODEC_WRDMA_QXM0_SHRAM_ST_ADDR(i)				(0x1640 + (0x4 * (i)))
+#define QAIF_WRDMA_QXM1_SHRAM_LEN(i)						(0x1700 + (0x4 * (i)))
+#define QAIF_CODEC_WRDMA_QXM1_SHRAM_LEN(i)					(0x1740 + (0x4 * (i)))
+#define QAIF_WRDMA_QXM0_SHRAM_LEN(i)						(0x1800 + (0x4 * (i)))
+#define QAIF_CODEC_WRDMA_QXM0_SHRAM_LEN(i)					(0x1840 + (0x4 * (i)))
+
+/* RDDMA
+ * v : ptr to qaif_variant
+ */
+#define QAIF_RDDMA_REG_ADDR(v, offset, chan) \
+	(v->rddma_reg_base + (offset) + v->rddma_stride * (chan))
+#define QAIF_RDDMA_CTL_REG(v, chan)							QAIF_RDDMA_REG_ADDR(v, 0x00, (chan))
+#define QAIF_RDDMA_CFG_REG(v, chan)							QAIF_RDDMA_REG_ADDR(v, 0x04, (chan))
+#define QAIF_RDDMA_BASE_ADDR_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x08, (chan))
+#define	QAIF_RDDMA_BUFF_LEN_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x10, (chan))
+#define QAIF_RDDMA_CURR_ADDR_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x14, (chan))
+#define	QAIF_RDDMA_PERIOD_LEN_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x1C, (chan))
+#define	QAIF_RDDMA_PERIOD_CNT_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x20, (chan))
+#define QAIF_RDDMA_SHRAM_WORDCNT_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x24, (chan))
+#define QAIF_RDDMA_FRAME_STATUS_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x28, (chan))
+#define QAIF_RDDMA_FRAME_STATUS_EXTN_REG(v, chan)			QAIF_RDDMA_REG_ADDR(v, 0x2C, (chan))
+#define QAIF_RDDMA_FRAME_STATUS_CLR_REG(v, chan)			QAIF_RDDMA_REG_ADDR(v, 0x30, (chan))
+#define QAIF_RDDMA_SET_BUFF_CNT_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x34, (chan))
+#define QAIF_RDDMA_SET_PERIOD_CNT_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x38, (chan))
+#define QAIF_RDDMA_STC_LSB_REG(v, chan)						QAIF_RDDMA_REG_ADDR(v, 0x3C, (chan))
+#define QAIF_RDDMA_STC_MSB_REG(v, chan)						QAIF_RDDMA_REG_ADDR(v, 0x40, (chan))
+#define QAIF_RDDMA_PERIOD_DET_STAT_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x44, (chan))
+#define QAIF_RDDMA_PERIOD_DET_CLR_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x48, (chan))
+#define QAIF_RDDMA_FORMAT_ERR_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x4C, (chan))
+#define QAIF_RDDMA_AHB_BYPASS_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x50, (chan))
+#define QAIF_RDDMA_SHUTDOWN_STAT_REG(v, chan)				QAIF_RDDMA_REG_ADDR(v, 0x54, (chan))
+#define QAIF_RDDMA_PADDING_CFG_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0x58, (chan))
+#define QAIF_RDDMA_STATUS_REG(v, chan)						QAIF_RDDMA_REG_ADDR(v, 0x60, (chan))
+#define QAIF_RDDMA_DBG_STATUS_REG(v, chan)					QAIF_RDDMA_REG_ADDR(v, 0xFF0, (chan))
+
+#define QAIF_CODEC_RDDMA_REG_ADDR(v, offset, chan) \
+	(v->codec_rddma_reg_base + (offset) + v->codec_rddma_stride * (chan))
+#define QAIF_CODEC_RDDMA_CTL_REG(v, chan)					QAIF_CODEC_RDDMA_REG_ADDR(v, 0x00, (chan))
+#define QAIF_CODEC_RDDMA_CFG_REG(v, chan)					QAIF_CODEC_RDDMA_REG_ADDR(v, 0x04, (chan))
+#define QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x08, (chan))
+#define QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x10, (chan))
+#define QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x14, (chan))
+#define QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x1C, (chan))
+#define QAIF_CODEC_RDDMA_PERIOD_CNT_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x20, (chan))
+#define QAIF_CODEC_RDDMA_SHRAM_WORDCNT_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x24, (chan))
+#define QAIF_CODEC_RDDMA_FRAME_STATUS_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x28, (chan))
+#define QAIF_CODEC_RDDMA_FRAME_STATUS_EXTN_REG(v, chan)		QAIF_CODEC_RDDMA_REG_ADDR(v, 0x2C, (chan))
+#define QAIF_CODEC_RDDMA_FRAME_STATUS_CLR_REG(v, chan)		QAIF_CODEC_RDDMA_REG_ADDR(v, 0x30, (chan))
+#define QAIF_CODEC_RDDMA_SET_BUFF_CNT_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x34, (chan))
+#define QAIF_CODEC_RDDMA_SET_PERIOD_CNT_REG(v, chan)		QAIF_CODEC_RDDMA_REG_ADDR(v, 0x38, (chan))
+#define QAIF_CODEC_RDDMA_STC_LSB_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x3C, (chan))
+#define QAIF_CODEC_RDDMA_STC_MSB_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x40, (chan))
+#define QAIF_CODEC_RDDMA_PERIOD_DET_STAT_REG(v, chan)		QAIF_CODEC_RDDMA_REG_ADDR(v, 0x44, (chan))
+#define QAIF_CODEC_RDDMA_PERIOD_DET_CLR_REG(v, chan)		QAIF_CODEC_RDDMA_REG_ADDR(v, 0x48, (chan))
+#define QAIF_CODEC_RDDMA_FORMAT_ERR_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x4C, (chan))
+#define QAIF_CODEC_RDDMA_AHB_BYPASS_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x50, (chan))
+#define QAIF_CODEC_RDDMA_SHUTDOWN_STAT_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x54, (chan))
+#define QAIF_CODEC_RDDMA_PADDING_CFG_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0x58, (chan))
+#define QAIF_CODEC_RDDMA_INTF_CFG_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x5C, (chan))
+#define QAIF_CODEC_RDDMA_STATUS_REG(v, chan)				QAIF_CODEC_RDDMA_REG_ADDR(v, 0x60, (chan))
+#define QAIF_CODEC_RDDMA_DBG_STATUS_REG(v, chan)			QAIF_CODEC_RDDMA_REG_ADDR(v, 0xFF0, (chan))
+#define QAIF_CODEC_RDDMA_INTF_DBG_STATUS_REG(v, chan)		QAIF_CODEC_RDDMA_REG_ADDR(v, 0xFF4, (chan))
+
+/* WRDMA
+ * v : ptr to qaif_variant
+ */
+#define QAIF_WRDMA_REG_ADDR(v, offset, chan) \
+	(v->wrdma_reg_base + (offset) + v->wrdma_stride * (chan))
+#define QAIF_WRDMA_CTL_REG(v, chan)							QAIF_WRDMA_REG_ADDR(v, 0x00, (chan))
+#define QAIF_WRDMA_CFG_REG(v, chan)							QAIF_WRDMA_REG_ADDR(v, 0x04, (chan))
+#define QAIF_WRDMA_BASE_ADDR_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x08, (chan))
+#define QAIF_WRDMA_BUFF_LEN_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
+#define QAIF_WRDMA_CURR_ADDR_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
+#define QAIF_WRDMA_PERIOD_LEN_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x1C, (chan))
+#define QAIF_WRDMA_PERIOD_CNT_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x20, (chan))
+#define QAIF_WRDMA_SHRAM_WORDCNT_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x24, (chan))
+#define QAIF_WRDMA_FRAME_STATUS_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x28, (chan))
+#define QAIF_WRDMA_FRAME_STATUS_EXTN_REG(v, chan)			QAIF_WRDMA_REG_ADDR(v, 0x2C, (chan))
+#define QAIF_WRDMA_FRAME_STATUS_CLR_REG(v, chan)			QAIF_WRDMA_REG_ADDR(v, 0x30, (chan))
+#define QAIF_WRDMA_SET_BUFF_CNT_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x34, (chan))
+#define QAIF_WRDMA_SET_PERIOD_CNT_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x38, (chan))
+#define QAIF_WRDMA_STC_LSB_REG(v, chan)						QAIF_WRDMA_REG_ADDR(v, 0x3C, (chan))
+#define QAIF_WRDMA_STC_MSB_REG(v, chan)						QAIF_WRDMA_REG_ADDR(v, 0x40, (chan))
+#define QAIF_WRDMA_PERIOD_DET_STAT_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x44, (chan))
+#define QAIF_WRDMA_PERIOD_DET_CLR_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x48, (chan))
+#define QAIF_WRDMA_FORMAT_ERR_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x4C, (chan))
+#define QAIF_WRDMA_AHB_BYPASS_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0x50, (chan))
+#define QAIF_WRDMA_SHUTDOWN_STAT_REG(v, chan)				QAIF_WRDMA_REG_ADDR(v, 0x54, (chan))
+#define QAIF_WRDMA_DBG_STATUS_REG(v, chan)					QAIF_WRDMA_REG_ADDR(v, 0xFF0, (chan))
+
+#define QAIF_CODEC_WRDMA_REG_ADDR(v, offset, chan) \
+	(v->codec_wrdma_reg_base + (offset) + v->codec_wrdma_stride * (chan))
+#define QAIF_CODEC_WRDMA_CTL_REG(v, chan)					QAIF_CODEC_WRDMA_REG_ADDR(v, 0x00, (chan))
+#define QAIF_CODEC_WRDMA_CFG_REG(v, chan)					QAIF_CODEC_WRDMA_REG_ADDR(v, 0x04, (chan))
+#define QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, chan)				QAIF_CODEC_WRDMA_REG_ADDR(v, 0x08, (chan))
+#define QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, chan)				QAIF_CODEC_WRDMA_REG_ADDR(v, 0x10, (chan))
+#define QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, chan)				QAIF_CODEC_WRDMA_REG_ADDR(v, 0x14, (chan))
+#define QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x1C, (chan))
+#define QAIF_CODEC_WRDMA_PERIOD_CNT_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x20, (chan))
+#define QAIF_CODEC_WRDMA_SHRAM_WORDCNT_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x24, (chan))
+#define QAIF_CODEC_WRDMA_FRAME_STATUS_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x28, (chan))
+#define QAIF_CODEC_WRDMA_FRAME_STATUS_EXTN_REG(v, chan)		QAIF_CODEC_WRDMA_REG_ADDR(v, 0x2C, (chan))
+#define QAIF_CODEC_WRDMA_FRAME_STATUS_CLR_REG(v, chan)		QAIF_CODEC_WRDMA_REG_ADDR(v, 0x30, (chan))
+#define QAIF_CODEC_WRDMA_SET_BUFF_CNT_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x34, (chan))
+#define QAIF_CODEC_WRDMA_SET_PERIOD_CNT_REG(v, chan)		QAIF_CODEC_WRDMA_REG_ADDR(v, 0x38, (chan))
+#define QAIF_CODEC_WRDMA_STC_LSB_REG(v, chan)				QAIF_CODEC_WRDMA_REG_ADDR(v, 0x3C, (chan))
+#define QAIF_CODEC_WRDMA_STC_MSB_REG(v, chan)				QAIF_CODEC_WRDMA_REG_ADDR(v, 0x40, (chan))
+#define QAIF_CODEC_WRDMA_PERIOD_DET_STAT_REG(v, chan)		QAIF_CODEC_WRDMA_REG_ADDR(v, 0x44, (chan))
+#define QAIF_CODEC_WRDMA_PERIOD_DET_CLR_REG(v, chan)		QAIF_CODEC_WRDMA_REG_ADDR(v, 0x48, (chan))
+#define QAIF_CODEC_WRDMA_FORMAT_ERR_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x4C, (chan))
+#define QAIF_CODEC_WRDMA_AHB_BYPASS_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x50, (chan))
+#define QAIF_CODEC_WRDMA_SHUTDOWN_STAT_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0x54, (chan))
+#define QAIF_CODEC_WRDMA_INTF_CFG_REG(v, chan)				QAIF_CODEC_WRDMA_REG_ADDR(v, 0x58, (chan))
+#define QAIF_CODEC_WRDMA_DBG_STATUS_REG(v, chan)			QAIF_CODEC_WRDMA_REG_ADDR(v, 0xFF0, (chan))
+#define QAIF_CODEC_WRDMA_INTF_DBG_STATUS_REG(v, chan)		QAIF_CODEC_WRDMA_REG_ADDR(v, 0xFF4, (chan))
+
+
+#define QAIF_EE_RDDMA_IRQ_REG_ADDR(v, dma_type, offset) \
+	(dma_type == QAIF_AIF_IRQ ? (v->rddma_irq_reg_base + (offset) + v->rddma_irq_stride * (v->ee)) : \
+	(v->codec_rddma_irq_reg_base + (offset) + v->codec_rddma_irq_stride * (v->ee)))
+
+/* RDDMA Period Interrupts */
+#define QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, i)				QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x00)
+#define QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, i)				QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x08)
+#define QAIF_EE_RDDMA_PERIOD_IRQ_RAW_STAT_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x10)
+#define QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, i)				QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x18)
+#define QAIF_EE_RDDMA_PERIOD_IRQ_FORCE_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x20)
+/* RDDMA Underflow Interrupts */
+#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x28)
+#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x30)
+#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_RAW_STAT_REG(v, i)		QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x38)
+#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x40)
+#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_FORCE_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x48)
+/* RDDMA Error Response Interrupts */
+#define QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, i)				QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x50)
+#define QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x58)
+#define QAIF_EE_RDDMA_ERR_RSP_IRQ_RAW_STAT_REG(v, i)		QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x60)
+#define QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, i)				QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x68)
+#define QAIF_EE_RDDMA_ERR_RSP_IRQ_FORCE_REG(v, i)			QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x70)
+
+#define QAIF_EE_WRDMA_IRQ_REG_ADDR(v, dma_type, offset) \
+	(dma_type == QAIF_AIF_IRQ ? (v->wrdma_irq_reg_base + (offset) + v->wrdma_irq_stride * (v->ee)) : \
+	(v->codec_wrdma_irq_reg_base + (offset) + v->codec_wrdma_irq_stride * (v->ee)))
+
+/* WRDMA Period Interrupts */
+#define QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, i)				QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x00)
+#define QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, i)				QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x08)
+#define QAIF_EE_WRDMA_PERIOD_IRQ_RAW_STAT_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x10)
+#define QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, i)				QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x18)
+#define QAIF_EE_WRDMA_PERIOD_IRQ_FORCE_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x20)
+/* WRDMA Overflow Interrupts */
+#define QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, i)				QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x28)
+#define QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x30)
+#define QAIF_EE_WRDMA_OVERFLOW_IRQ_RAW_STAT_REG(v, i)		QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x38)
+#define QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x40)
+#define QAIF_EE_WRDMA_OVERFLOW_IRQ_FORCE_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x48)
+/* WRDMA Error Response Interrupts */
+#define QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, i)				QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x50)
+#define QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x58)
+#define QAIF_EE_WRDMA_ERR_RSP_IRQ_RAW_STAT_REG(v, i)		QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x60)
+#define QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, i)				QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x68)
+#define QAIF_EE_WRDMA_ERR_RSP_IRQ_FORCE_REG(v, i)			QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x70)
+
+
+#define QAIF_DMACFG_REG(v, chan, dir, dai_id) \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, CFG) : \
+	__QAIF_DMA_REG(v, chan, dir, CFG))
+
+#define QAIF_DMACTL_REG(v, chan, dir, dai_id) \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, CTL) : \
+	__QAIF_DMA_REG(v, chan, dir, CTL))
+
+#define QAIF_DMABUFF_REG(v, chan, dir, dai_id) \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, BUFF_LEN) : \
+	__QAIF_DMA_REG(v, chan, dir, BUFF_LEN))
+
+#define QAIF_DMACURR_REG(v, chan, dir, dai_id) \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, CURR_ADDR) : \
+	__QAIF_DMA_REG(v, chan, dir, CURR_ADDR))
+
+#define QAIF_DMAPER_REG(v, chan, dir, dai_id)  \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, PERIOD_CNT) : \
+	__QAIF_DMA_REG(v, chan, dir, PERIOD_CNT))
+
+#define QAIF_DMAPER_LEN_REG(v, chan, dir, dai_id)  \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, PERIOD_LEN) : \
+	__QAIF_DMA_REG(v, chan, dir, PERIOD_LEN))
+
+#define QAIF_DMABASE_REG(v, chan, dir, dai_id) \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_DMA_REG(v, chan, dir, BASE_ADDR) : \
+	__QAIF_DMA_REG(v, chan, dir, BASE_ADDR))
+
+#define __QAIF_CDC_DMA_REG(v, chan, dir, reg) \
+		(((dir) ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
+			__QAIF_CDC_RDDMA_REG(v, chan, reg) : \
+			__QAIF_CDC_WRDMA_REG(v, chan, reg))
+
+#define __QAIF_DMA_REG(v, chan, dir, reg)  \
+	(((dir) ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
+		(QAIF_RDDMA_##reg##_REG(v, chan)) : \
+		QAIF_WRDMA_##reg##_REG(v, chan))
+
+#define __QAIF_CDC_RDDMA_REG(v, chan, reg) \
+			QAIF_CODEC_RDDMA_##reg##_REG(v, chan)
+
+#define __QAIF_CDC_WRDMA_REG(v, chan, reg) \
+			QAIF_CODEC_WRDMA_##reg##_REG(v, chan)
+
+#define QAIF_SID_MAP_REG(dir, dai_id) \
+	(is_cif_dma_port(dai_id) ? \
+	__QAIF_CDC_SID_MAP_REG(dir) : \
+	__QAIF_SID_MAP_REG(dir))
+
+#define __QAIF_SID_MAP_REG(dir)  \
+	(((dir) ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
+		QAIF_RDDMA_SID_MAP_REG : \
+		QAIF_WRDMA_SID_MAP_REG)
+
+#define __QAIF_CDC_SID_MAP_REG(dir)  \
+	(((dir) ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
+		QAIF_CODEC_RDDMA_SID_MAP_REG : \
+		QAIF_CODEC_WRDMA_SID_MAP_REG)
+
+#endif /* __QAIF_REG_H__ */
-- 
2.34.1


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

* [PATCH 5/13] ASoC: qcom: Add QAIF shared data structures and variant interface
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (3 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 4/13] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:51   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 6/13] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add qaif.h to define the common data structures used by the QAIF CPU DAI
and PCM platform drivers.

QAIF exposes AIF DMA paths for external audio interfaces and CIF DMA paths
for the internal codec interface. Both paths need shared state for DMA
channel allocation, regmap fields, interrupt dispatch, SHRAM buffer
tracking and per-stream runtime data.

Introduce the common QAIF driver data, AIF and CIF register-field
descriptions, IRQ mapping helpers and the SoC variant descriptor. The
variant descriptor provides the register layout, DMA channel counts, clock
names, DAI table and platform callbacks needed by later QAIF driver code.

Also define the QAIF-private MI2S port IDs used for the senary and
septenary interfaces.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif.h | 505 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 505 insertions(+)
 create mode 100644 sound/soc/qcom/qaif.h

diff --git a/sound/soc/qcom/qaif.h b/sound/soc/qcom/qaif.h
new file mode 100644
index 000000000000..ded885a680e9
--- /dev/null
+++ b/sound/soc/qcom/qaif.h
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * qaif.h -- Shared data structures and definitions for the Qualcomm Audio Interface (QAIF)
+ */
+#ifndef __QAIF_H__
+#define __QAIF_H__
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <dt-bindings/sound/qcom,qaif.h>
+
+#define LPASS_MAX_MI2S_PORTS			(8)
+#define LPASS_MAX_AIF_DMA_IDX			(8)
+#define LPASS_MAX_CIF_DMA_IDX		(8)
+#define QAIF_CIF_DMA_INTF_ONE_CHANNEL		(0x01)
+#define QAIF_CIF_DMA_INTF_TWO_CHANNEL		(0x03)
+#define QAIF_CIF_DMA_INTF_FOUR_CHANNEL		(0x0F)
+#define QAIF_CIF_DMA_INTF_SIX_CHANNEL		(0x3F)
+#define QAIF_CIF_DMA_INTF_EIGHT_CHANNEL		(0xFF)
+
+#define QAIF_DMACTL_ENABLE_ON		1
+#define QAIF_DMACTL_ENABLE_OFF		0
+
+#define QAIF_DMACTL_DYNCLK_ON		1
+#define QAIF_DMACTL_DYNCLK_OFF		0
+
+#define QAIF_AIF_CTL_ENABLE_ON		1
+#define QAIF_AIF_CTL_ENABLE_OFF		0
+
+#define QAIF_CIF_16BIT_UNPACK_ENABLE		1
+#define QAIF_CIF_16BIT_UNPACK_DISABLE		0
+
+#define QAIF_CIF_DMA_FS_SEL_DEFAULT			0
+
+#define QAIF_DMA_DEFAULT_BIT_WIDTH			32
+
+#define QAIF_DMA_BYTES_TO_WORDS_SHIFT			3
+
+#define QAIF_MAX_VARIANT_CLKS				32
+
+#define QAIF_DMA_CLK_RATE_MULTIPLIER			100
+
+#define QAIF_MAX_AIF_CFG_CNT (LPASS_MAX_AIF_DMA_IDX/2)
+
+#define QAIF_AUD_INTF_CTL_MONO				1  /* Mono Mode True */
+#define QAIF_AUD_INTF_CTL_STEREO			0  /* Mono Mode False */
+
+#define QAIF_AIF_SAMPLE_WIDTH(bits)			((bits) - 1)
+#define QAIF_AIF_SLOT_WIDTH(bits)			((bits) - 1)
+
+#define QAIF_DMACTL_WM_5					4
+#define QAIF_DMACTL_WM_8					7
+#define QAIF_DMACTL_BURSTEN					1
+
+#define QAIF_MAX_LANES						8
+
+
+enum qxm_sel {
+	QXM0 = 0,
+	QXM1 = 1,
+	MAX_QXM_TYPE,
+};
+
+static inline bool is_cif_dma_port(int dai_id)
+{
+	switch (dai_id) {
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		return true;
+	}
+	return false;
+}
+
+enum qaif_type_t {
+	QAIF_INVALID = -1,
+	QAIF = 0,
+	QAIF_VA,
+	QAIF_MAX_TYPES
+};
+
+enum qaif_irq_type {
+	QAIF_AIF_IRQ = 0,
+	QAIF_CIF_IRQ = 1,
+	QAIF_AUD_INTF_IRQ = 2,
+	QAIF_IRQ_MAX = 3
+};
+
+enum qaif_dma_type {
+	QAIF_AIF_DMA = 0,
+	QAIF_CIF_DMA = 1,
+	DMA_TYPE_MAX
+};
+
+struct qaif_dmactl {
+	/* AUDIO_CORE_QAIF_CODEC_xDMAa_CTL */
+	struct regmap_field *enable;
+	struct regmap_field *reset;
+
+	/* AUDIO_CORE_QAIF_CODEC_xDMAa_CFG */
+	struct regmap_field *num_ot; /* outstanding transactions */
+	struct regmap_field *dma_dyncclk;
+	struct regmap_field *burst16;
+	struct regmap_field *burst8;
+	struct regmap_field *burst4;
+	struct regmap_field *burst2;
+	struct regmap_field *burst1;
+	struct regmap_field *shram_wm; /* SHRAM watermark */
+
+};
+
+struct qaif_cdc_intfctl {
+	/* AUDIO_CORE_QAIF_CODEC_xDMAa_INTF_CFG */
+	struct regmap_field *active_ch_en;
+	struct regmap_field *fs_sel;
+	struct regmap_field *fs_delay;
+	struct regmap_field *fs_out_gate;
+	struct regmap_field *intf_dyncclk;
+	struct regmap_field *en_16bit_unpack;
+};
+
+struct qaif_aud_intfctl {
+	/* AUDIO_CORE_QAIF_AUD_INTFa_SYNC_CFG */
+	struct regmap_field *inv_sync;
+	struct regmap_field *sync_delay;
+	struct regmap_field *sync_mode;
+	struct regmap_field *sync_src;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_BIT_WIDTH_CFG */
+	struct regmap_field *slot_width_rx;
+	struct regmap_field *slot_width_tx;
+	struct regmap_field *sample_width_rx;
+	struct regmap_field *sample_width_tx;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_MI2S_CFG */
+	struct regmap_field *mono_mode_rx;
+	struct regmap_field *mono_mode_tx;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_LANE_CFG */
+	struct regmap_field *lane_en;           /* Lane enable mask (bits 8-15) */
+	struct regmap_field *lane_dir;          /* Lane direction mask (bits 0-7, 0=TX, 1=RX) */
+	struct regmap_field *loopback_en;
+	struct regmap_field *ctrl_data_oe;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_RX */
+	struct regmap_field *slot_en_rx_mask;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_TX */
+	struct regmap_field *slot_en_tx_mask;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_CFG */
+	struct regmap_field *full_cycle_en;
+	/* AUDIO_CORE_QAIF_AUD_INTFa_FRAME_CFG */
+	struct regmap_field *bits_per_lane;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_CTL */
+	struct regmap_field *enable;
+	struct regmap_field *enable_tx;
+	struct regmap_field *enable_rx;
+	struct regmap_field *reset;
+	struct regmap_field *reset_tx;
+	struct regmap_field *reset_rx;
+};
+
+/* Lane configuration structure */
+struct qaif_lane_config {
+	u32 enable;     /* 1 = enabled, 0 = disabled */
+	u32 direction;  /* 0 = TX_SPKR, 1 = RX_MIC */
+};
+
+/*
+ * AIF interface protocol mode — set by the DT compatible string and
+ * used to select mode-specific register defaults and property parsing.
+ */
+enum qaif_aif_mode {
+	QAIF_AIF_MODE_PCM,
+	QAIF_AIF_MODE_TDM,
+	QAIF_AIF_MODE_MI2S,
+};
+
+/* QAIF Audio Interface Configuration Structure */
+struct qaif_aif_config {
+	/* Interface protocol mode derived from DT compatible */
+	enum qaif_aif_mode mode;
+	/* Sync configuration */
+	u32 sync_mode;
+	u32 sync_src;
+	u32 invert_sync;
+	u32 sync_delay;
+	/* Slot and sample width configuration */
+	u32 slot_width_rx;
+	u32 slot_width_tx;
+	u32 sample_width_rx;
+	u32 sample_width_tx;
+	/* Slot enable masks (32-bit masks for 32 slots) */
+	u32 slot_en_rx_mask;
+	u32 slot_en_tx_mask;
+	/* Control configuration */
+	u32 loopback_en;
+	u32 ctrl_data_oe;
+	/* Lane configuration */
+	u32 num_lanes;          /* Number of lanes configured */
+	struct qaif_lane_config lane_cfg[QAIF_MAX_LANES];
+	u32 lane_en_mask;
+	u32 lane_dir_mask;
+	/* Frame configuration */
+	u32 full_cycle_en;
+	u32 bits_per_lane;
+};
+
+struct qaif_pcm_data {
+	int stream_dma_idx;
+};
+
+struct qaif_dma_mem_info {
+	dma_addr_t dma_addr;
+	size_t alloc_size;
+	void *vaddr;
+};
+
+struct qaif_dmaidx_dai_map {
+	unsigned int dai_id;
+};
+
+/* Both the CPU DAI and platform drivers will access this data */
+struct qaif_drv_data {
+
+	/* MI2S bit clock (derived from system clock by a divider) */
+	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
+
+	/* SOC specific clock list */
+	struct clk_bulk_data *clks;
+	int num_clks;
+
+	struct clk *aud_dma_clk;
+	struct clk *aud_dma_mem_clk;
+
+	/* Qualcomm audio interface (QAIF) registers */
+	void __iomem *audio_qaif;
+
+	/* regmap backed by the Qualcomm audio interface (QAIF) registers */
+	struct regmap *audio_qaif_map;
+
+	/* interrupts from the Qualcomm audio interface (QAIF) */
+	int audio_qaif_irq;
+
+	/* QAIF init config refcount*/
+	unsigned int qaif_init_ref_cnt;
+
+	/* SOC specific variations in the QAIF IP integration */
+	const struct qaif_variant *variant;
+
+	/* Runtime-allocated regmap field handles (populated at probe) */
+	struct qaif_dmactl *aif_rd_dmactl;
+	struct qaif_dmactl *aif_wr_dmactl;
+	struct qaif_dmactl *cif_rd_dmactl;
+	struct qaif_dmactl *cif_wr_dmactl;
+	struct qaif_aud_intfctl *aif_intfctl;
+	struct qaif_cdc_intfctl *cif_rddma_intfctl;
+	struct qaif_cdc_intfctl *cif_wrdma_intfctl;
+	struct qaif_aif_config aif_intf_cfg[QAIF_MAX_AIF_CFG_CNT];
+
+	/* bit map to keep track of dma idx allocations */
+	unsigned long aif_dma_idx_bit_map;
+	unsigned long cif_dma_idx_bit_map;
+
+	/* used it for handling interrupt per dma channel */
+	struct snd_pcm_substream *aif_substream[LPASS_MAX_AIF_DMA_IDX];
+	struct snd_pcm_substream *cif_substream[LPASS_MAX_CIF_DMA_IDX];
+
+	u32 smmu_csid_bits;
+
+	/* DMA info handle per stream/dma idx */
+	struct qaif_dma_mem_info *aif_dma_heap[LPASS_MAX_AIF_DMA_IDX];
+	struct qaif_dma_mem_info *cif_dma_heap[LPASS_MAX_CIF_DMA_IDX];
+
+};
+
+enum qaif_summary_irq_bitmask {
+	QAIF_SUMMARY_BITMASK_AIF_PERIOD_RDDMA		= BIT(0),
+	QAIF_SUMMARY_BITMASK_AIF_UNDERFLOW_RDDMA	= BIT(1),
+	QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_RDDMA		= BIT(2),
+	QAIF_SUMMARY_BITMASK_AIF_PERIOD_WRDMA		= BIT(3),
+	QAIF_SUMMARY_BITMASK_AIF_OVERFLOW_WRDMA		= BIT(4),
+	QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_WRDMA		= BIT(5),
+
+	QAIF_SUMMARY_BITMASK_AUD_OVERFLOW			= BIT(6),
+	QAIF_SUMMARY_BITMASK_AUD_UNDERFLOW			= BIT(7),
+
+	QAIF_SUMMARY_BITMASK_RATE_DET				= BIT(8),
+	QAIF_SUMMARY_BITMASK_VFR					= BIT(9),
+	QAIF_SUMMARY_BITMASK_GRP					= BIT(10),
+	QAIF_SUMMARY_BITMASK_RDDMA_OVERLAP			= BIT(11),
+	QAIF_SUMMARY_BITMASK_WRDMA_OVERLAP			= BIT(12),
+	QAIF_SUMMARY_BITMASK_INTF_OVERLAP			= BIT(13),
+	QAIF_SUMMARY_BITMASK_GRP_OVERLAP			= BIT(14),
+
+	QAIF_SUMMARY_BITMASK_CIF_OVERLAP_RDDMA		= BIT(15),
+	QAIF_SUMMARY_BITMASK_CIF_OVERLAP_WRDMA		= BIT(17),
+	QAIF_SUMMARY_BITMASK_CIF_PERIOD_RDDMA		= BIT(18),
+	QAIF_SUMMARY_BITMASK_CIF_UNDERFLOW_RDDMA	= BIT(19),
+	QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_RDDMA		= BIT(20),
+	QAIF_SUMMARY_BITMASK_CIF_PERIOD_WRDMA		= BIT(24),
+	QAIF_SUMMARY_BITMASK_CIF_OVERFLOW_WRDMA		= BIT(25),
+	QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_WRDMA		= BIT(26)
+
+};
+
+enum qaif_client_status_register_bitmask_info {
+	QAIF_BITMASK_GROUP_INF			= 0x400,
+	QAIF_BITMASK_AIF_RDDMA_WRDMA	= 0x3F,
+	QAIF_BITMASK_CIF_RDDMA_WRDMA	= 0x71c0000,
+	QAIF_BITMASK_DP_RDDMA			= 0xe00000,
+	QAIF_BITMASK_AUD_INF			= 0xC0,
+};
+
+struct qaif_irq_map {
+	int client_id;
+	u32 mask;
+	irqreturn_t (*client_irq_handler)(struct qaif_drv_data *drvdata, u32 irq_status);
+};
+
+enum dma_type {
+	DMA_TYPE_RDDMA,
+	DMA_TYPE_WRDMA
+};
+
+enum qaif_irq {
+	QAIF_IRQ_PERIOD,
+	QAIF_IRQ_OVERFLOW,
+	QAIF_IRQ_UNDERFLOW,
+	QAIF_IRQ_ERROR
+};
+
+enum qaif_client_info {
+	QAIF_CLIENT_ID_GROUP_INF = 0,
+	QAIF_CLIENT_ID_AIF_DMA	= 1,
+	QAIF_CLIENT_ID_CIF_DMA	= 2,
+	QAIF_CLIENT_ID_DP_DMA	 = 3,
+	QAIF_CLIENT_ID_AUD_INF	= 4,
+	QAIF_CLIENT_ID_MAX
+};
+
+struct qaif_variant {
+	u32 ee;
+	u32 qaif_type;
+
+	u32 num_rddma;
+	u32 num_wrdma;
+	u32 wrdma_start;
+
+	u32 num_codec_rddma;	/* RX */
+	u32 num_codec_wrdma;	/* TX */
+	u32 codec_wrdma_start;
+	u32 num_intf;
+
+	u32 rddma_reg_base;
+	u32 rddma_stride;
+	u32 codec_rddma_reg_base;
+	u32 codec_rddma_stride;
+
+	u32 wrdma_reg_base;
+	u32 wrdma_stride;
+	u32 codec_wrdma_reg_base;
+	u32 codec_wrdma_stride;
+
+	u32 rddma_irq_reg_base;
+	u32 rddma_irq_stride;
+	u32 codec_rddma_irq_reg_base;
+	u32 codec_rddma_irq_stride;
+
+	u32 wrdma_irq_reg_base;
+	u32 wrdma_irq_stride;
+	u32 codec_wrdma_irq_reg_base;
+	u32 codec_wrdma_irq_stride;
+
+	u32 qxm_type;
+	u32 rd_len;
+	u32 rddma_shram_len;
+	u32 rddma_shram_start_addr[DMA_TYPE_MAX];
+	u32 wr_len;
+	u32 wrdma_shram_len;
+	u32 wrdma_shram_start_addr[DMA_TYPE_MAX];
+
+	/* AIF RDDMA register fields */
+	struct reg_field rddma_enable;
+	struct reg_field rddma_reset;
+	struct reg_field rddma_num_ot;
+	struct reg_field rddma_dma_dyncclk;
+	struct reg_field rddma_burst16;
+	struct reg_field rddma_burst8;
+	struct reg_field rddma_burst4;
+	struct reg_field rddma_burst2;
+	struct reg_field rddma_burst1;
+	struct reg_field rddma_shram_wm;
+
+	/* AIF WRDMA register fields */
+	struct reg_field wrdma_enable;
+	struct reg_field wrdma_reset;
+	struct reg_field wrdma_num_ot;
+	struct reg_field wrdma_dma_dyncclk;
+	struct reg_field wrdma_burst16;
+	struct reg_field wrdma_burst8;
+	struct reg_field wrdma_burst4;
+	struct reg_field wrdma_burst2;
+	struct reg_field wrdma_burst1;
+	struct reg_field wrdma_shram_wm;
+
+	/* CODEC RDDMA register fields */
+	struct reg_field cif_rddma_enable;
+	struct reg_field cif_rddma_reset;
+	struct reg_field cif_rddma_num_ot;
+	struct reg_field cif_rddma_dma_dyncclk;
+	struct reg_field cif_rddma_burst16;
+	struct reg_field cif_rddma_burst8;
+	struct reg_field cif_rddma_burst4;
+	struct reg_field cif_rddma_burst2;
+	struct reg_field cif_rddma_burst1;
+	struct reg_field cif_rddma_shram_wm;
+	struct reg_field cif_rddma_active_ch_en;
+	struct reg_field cif_rddma_fs_sel;
+	struct reg_field cif_rddma_fs_delay;
+	struct reg_field cif_rddma_fs_out_gate;
+	struct reg_field cif_rddma_intf_dyncclk;
+	struct reg_field cif_rddma_en_16bit_unpack;
+
+	/* CODEC WRDMA register fields */
+	struct reg_field cif_wrdma_enable;
+	struct reg_field cif_wrdma_reset;
+	struct reg_field cif_wrdma_num_ot;
+	struct reg_field cif_wrdma_dma_dyncclk;
+	struct reg_field cif_wrdma_burst16;
+	struct reg_field cif_wrdma_burst8;
+	struct reg_field cif_wrdma_burst4;
+	struct reg_field cif_wrdma_burst2;
+	struct reg_field cif_wrdma_burst1;
+	struct reg_field cif_wrdma_shram_wm;
+	struct reg_field cif_wrdma_active_ch_en;
+	struct reg_field cif_wrdma_fs_sel;
+	struct reg_field cif_wrdma_fs_delay;
+	struct reg_field cif_wrdma_fs_out_gate;
+	struct reg_field cif_wrdma_intf_dyncclk;
+	struct reg_field cif_wrdma_en_16bit_unpack;
+
+	/* Regmap fields of AIF interface registers bitfields */
+	struct reg_field aif_inv_sync;
+	struct reg_field aif_sync_delay;
+	struct reg_field aif_sync_mode;
+	struct reg_field aif_sync_src;
+	struct reg_field aif_sample_width_rx;
+	struct reg_field aif_sample_width_tx;
+	struct reg_field aif_slot_width_rx;
+	struct reg_field aif_slot_width_tx;
+	struct reg_field aif_bits_per_lane;
+	struct reg_field aif_slot_en_tx_mask;
+	struct reg_field aif_slot_en_rx_mask;
+	struct reg_field aif_loopback_en;
+	struct reg_field aif_ctrl_data_oe;
+	struct reg_field aif_lane_en;
+	struct reg_field aif_lane_dir;
+	struct reg_field aif_mono_mode_rx;
+	struct reg_field aif_mono_mode_tx;
+	struct reg_field aif_full_cycle_en;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_CTL bitfields */
+	struct reg_field aif_enable;
+	struct reg_field aif_enable_tx;
+	struct reg_field aif_enable_rx;
+	struct reg_field aif_reset;
+	struct reg_field aif_reset_tx;
+	struct reg_field aif_reset_rx;
+
+	/* Platform-specific data */
+	const char **clk_name;
+	int num_clks;
+	struct snd_soc_dai_driver *dai_driver;
+	int num_dai;
+	const char **dai_osr_clk_names;
+	const char **dai_bit_clk_names;
+
+	/* Platform-specific function pointers */
+	int (*init)(struct platform_device *pdev);
+	int (*exit)(struct platform_device *pdev);
+	int (*alloc_stream_dma_idx)(struct qaif_drv_data *data, int direction, unsigned int dai_id);
+	int (*free_stream_dma_idx)(struct qaif_drv_data *data, int chan, unsigned int dai_id);
+	int (*get_dma_idx)(unsigned int dai_id);
+
+};
+
+/* External DAI ops structures defined in qaif-cpu.c */
+extern const struct snd_soc_dai_ops asoc_qcom_qaif_cif_dai_ops;
+extern const struct snd_soc_dai_ops asoc_qcom_qaif_aif_cpu_dai_ops;
+
+/* Platform driver functions defined in qaif-cpu.c */
+int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev);
+int asoc_qcom_qaif_platform_register(struct platform_device *pdev);
+void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev);
+void asoc_qcom_qaif_cpu_platform_shutdown(struct platform_device *pdev);
+
+#endif /* __QAIF_H__ */
-- 
2.34.1


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

* [PATCH 6/13] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (4 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 5/13] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:37 ` [PATCH 7/13] ASoC: qcom: Add QAIF AIF " Harendra Gautam
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add DAI operations for the QAIF Codec Interface (CIF), which connects the
QAIF DMA engine to the internal Bolero digital codec.

The CIF CDC DMA DAIs need to program the interface configuration for each
allocated RDDMA or WRDMA channel. Without these ops, the driver cannot
configure the active channel mask or enable the interface control bits
required when starting a playback or capture stream.

Initialize the CIF DMA and interface regmap fields, program the active
channel mask from hw_params(), and update the CIF interface controls from
trigger() for stream start, stop, suspend, resume and pause transitions.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif-cpu.c | 315 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 315 insertions(+)
 create mode 100644 sound/soc/qcom/qaif-cpu.c

diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
new file mode 100644
index 000000000000..8eedbccd8805
--- /dev/null
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * qaif-cpu.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include "qaif-reg.h"
+#include "qaif.h"
+#include "common.h"
+
+
+static int qaif_cif_cpu_init_bitfields(struct device *dev,
+			struct regmap *map)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_dmactl *rd_dmactl;
+	struct qaif_dmactl *wr_dmactl;
+	struct qaif_cdc_intfctl *rd_intfctl;
+	struct qaif_cdc_intfctl *wr_intfctl;
+
+	rd_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL);
+	if (!rd_dmactl)
+		return -ENOMEM;
+
+	wr_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL);
+	if (!wr_dmactl)
+		return -ENOMEM;
+
+	rd_intfctl = devm_kzalloc(dev, sizeof(struct qaif_cdc_intfctl), GFP_KERNEL);
+	if (!rd_intfctl)
+		return -ENOMEM;
+
+	wr_intfctl = devm_kzalloc(dev, sizeof(struct qaif_cdc_intfctl), GFP_KERNEL);
+	if (!wr_intfctl)
+		return -ENOMEM;
+
+	/*
+	 * Bulk-allocate CIF RDDMA dmactl fields.
+	 * Order must match struct qaif_dmactl member order:
+	 * enable, reset, num_ot, dma_dyncclk, burst16, burst8, burst4, burst2, burst1, shram_wm
+	 */
+	{
+		const struct reg_field cif_rd_dmactl_fields[] = {
+			v->cif_rddma_enable,
+			v->cif_rddma_reset,
+			v->cif_rddma_num_ot,
+			v->cif_rddma_dma_dyncclk,
+			v->cif_rddma_burst16,
+			v->cif_rddma_burst8,
+			v->cif_rddma_burst4,
+			v->cif_rddma_burst2,
+			v->cif_rddma_burst1,
+			v->cif_rddma_shram_wm,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&rd_dmactl->enable,
+					cif_rd_dmactl_fields,
+					ARRAY_SIZE(cif_rd_dmactl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating CIF RDDMA dmactl regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Bulk-allocate CIF RDDMA intfctl fields.
+	 * Order must match struct qaif_cdc_intfctl member order:
+	 * active_ch_en, fs_sel, fs_delay, fs_out_gate, intf_dyncclk, en_16bit_unpack
+	 */
+	{
+		const struct reg_field cif_rd_intfctl_fields[] = {
+			v->cif_rddma_active_ch_en,
+			v->cif_rddma_fs_sel,
+			v->cif_rddma_fs_delay,
+			v->cif_rddma_fs_out_gate,
+			v->cif_rddma_intf_dyncclk,
+			v->cif_rddma_en_16bit_unpack,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&rd_intfctl->active_ch_en,
+					cif_rd_intfctl_fields,
+					ARRAY_SIZE(cif_rd_intfctl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating CIF RDDMA intfctl regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Bulk-allocate CIF WRDMA dmactl fields.
+	 * Order must match struct qaif_dmactl member order:
+	 * enable, reset, num_ot, dma_dyncclk, burst16, burst8, burst4, burst2, burst1, shram_wm
+	 */
+	{
+		const struct reg_field cif_wr_dmactl_fields[] = {
+			v->cif_wrdma_enable,
+			v->cif_wrdma_reset,
+			v->cif_wrdma_num_ot,
+			v->cif_wrdma_dma_dyncclk,
+			v->cif_wrdma_burst16,
+			v->cif_wrdma_burst8,
+			v->cif_wrdma_burst4,
+			v->cif_wrdma_burst2,
+			v->cif_wrdma_burst1,
+			v->cif_wrdma_shram_wm,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&wr_dmactl->enable,
+					cif_wr_dmactl_fields,
+					ARRAY_SIZE(cif_wr_dmactl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating CIF WRDMA dmactl regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Bulk-allocate CIF WRDMA intfctl fields.
+	 * Order must match struct qaif_cdc_intfctl member order:
+	 * active_ch_en, fs_sel, fs_delay, fs_out_gate, intf_dyncclk, en_16bit_unpack
+	 */
+	{
+		const struct reg_field cif_wr_intfctl_fields[] = {
+			v->cif_wrdma_active_ch_en,
+			v->cif_wrdma_fs_sel,
+			v->cif_wrdma_fs_delay,
+			v->cif_wrdma_fs_out_gate,
+			v->cif_wrdma_intf_dyncclk,
+			v->cif_wrdma_en_16bit_unpack,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&wr_intfctl->active_ch_en,
+					cif_wr_intfctl_fields,
+					ARRAY_SIZE(cif_wr_intfctl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating CIF WRDMA intfctl regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	drvdata->cif_rd_dmactl = rd_dmactl;
+	drvdata->cif_wr_dmactl = wr_dmactl;
+	drvdata->cif_rddma_intfctl = rd_intfctl;
+	drvdata->cif_wrdma_intfctl = wr_intfctl;
+
+	return 0;
+}
+
+static struct qaif_cdc_intfctl *qaif_get_cif_intfctl_handle(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	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 qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	unsigned int dai_id = cpu_dai->driver->id;
+	struct qaif_cdc_intfctl *intfctl = NULL;
+
+	if (!v) {
+		dev_err(soc_runtime->dev, "No variant data\n");
+		return intfctl;
+	}
+
+	switch (dai_id) {
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+		intfctl = drvdata->cif_rddma_intfctl;
+		break;
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		intfctl = drvdata->cif_wrdma_intfctl;
+		break;
+	default:
+		dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id);
+		break;
+	}
+	return intfctl;
+}
+
+static int qaif_cif_daiops_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	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 qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_cdc_intfctl *intfctl = NULL;
+	unsigned int dai_id = cpu_dai->driver->id;
+	int ret;
+	unsigned int regval;
+	unsigned int channels = params_channels(params);
+	int idx;
+
+	switch (channels) {
+	case 1:
+		regval = QAIF_CIF_DMA_INTF_ONE_CHANNEL;
+		break;
+	case 2:
+		regval = QAIF_CIF_DMA_INTF_TWO_CHANNEL;
+		break;
+	case 4:
+		regval = QAIF_CIF_DMA_INTF_FOUR_CHANNEL;
+		break;
+	case 6:
+		regval = QAIF_CIF_DMA_INTF_SIX_CHANNEL;
+		break;
+	case 8:
+		regval = QAIF_CIF_DMA_INTF_EIGHT_CHANNEL;
+		break;
+	default:
+		dev_err(soc_runtime->dev, "invalid PCM config\n");
+		return -EINVAL;
+	}
+
+	intfctl = qaif_get_cif_intfctl_handle(substream, dai);
+	if (!intfctl) {
+		dev_err(soc_runtime->dev, "Invalid intfctl: %d\n", dai_id);
+		return -EINVAL;
+	}
+	idx = v->get_dma_idx(dai_id);
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "Invalid DMA index: %d\n", idx);
+		return -EINVAL;
+	}
+	ret = regmap_fields_write(intfctl->active_ch_en, idx, regval);
+	if (ret) {
+		dev_err(soc_runtime->dev,
+			"error writing to intfctl active_ch_en reg field: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qaif_cif_daiops_trigger(struct snd_pcm_substream *substream,
+				    int cmd, struct snd_soc_dai *dai)
+{
+	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 qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	unsigned int dai_id = cpu_dai->driver->id;
+	struct qaif_cdc_intfctl *intfctl = NULL;
+	int ret = 0, idx;
+
+	intfctl = qaif_get_cif_intfctl_handle(substream, dai);
+	if (!intfctl) {
+		dev_err(soc_runtime->dev, "Invalid intfctl: %d\n", dai_id);
+		return -EINVAL;
+	}
+	idx = v->get_dma_idx(dai_id);
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "Invalid DMA index: %d\n", idx);
+		return -EINVAL;
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = regmap_fields_write(intfctl->intf_dyncclk, idx, QAIF_DMACTL_DYNCLK_ON);
+		if (ret) {
+			dev_err(soc_runtime->dev, "error writing to dmactl intf_dyncclk reg field: %d\n", ret);
+			return ret;
+		}
+		ret = regmap_fields_write(intfctl->fs_sel, idx, QAIF_CIF_DMA_FS_SEL_DEFAULT);
+		if (ret) {
+			dev_err(soc_runtime->dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret);
+			return ret;
+		}
+
+		ret = regmap_fields_write(intfctl->en_16bit_unpack, idx, QAIF_CIF_16BIT_UNPACK_ENABLE);
+		if (ret) {
+			dev_err(soc_runtime->dev, "error writing to dmactl en_16bit_unpack reg field: %d\n", ret);
+			return ret;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = regmap_fields_write(intfctl->intf_dyncclk, idx, QAIF_DMACTL_DYNCLK_OFF);
+		if (ret) {
+			dev_err(soc_runtime->dev, "error writing to dmactl intf_dyncclk reg field: %d\n", ret);
+			return ret;
+		}
+		ret = regmap_fields_write(intfctl->en_16bit_unpack, idx, QAIF_CIF_16BIT_UNPACK_DISABLE);
+		if (ret) {
+			dev_err(soc_runtime->dev, "error writing to dmactl en_16bit_unpack reg field: %d\n", ret);
+			return ret;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd);
+		break;
+	}
+	return ret;
+}
+
+const struct snd_soc_dai_ops asoc_qcom_qaif_cif_dai_ops = {
+	.hw_params	= qaif_cif_daiops_hw_params,
+	.trigger	= qaif_cif_daiops_trigger,
+};
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cif_dai_ops);
-- 
2.34.1


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

* [PATCH 7/13] ASoC: qcom: Add QAIF AIF DAI ops
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (5 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 6/13] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:52   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 8/13] ASoC: qcom: Add generic of_xlate_dai_name helper to common Harendra Gautam
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add the CPU DAI operations and regmap field setup needed to use the QAIF
AIF (MI2S) path.

The existing QAIF CPU support does not provide the AIF callbacks needed to
configure and control MI2S playback and capture streams. Add the AIF-
specific register field allocation and DAI callbacks to program the
AUD_INTF configuration, set the MI2S bit clock, configure the codec DAI TDM
format, and enable or disable the interface during stream triggers.

This allows QAIF to route playback through RDDMA channels and capture
through WRDMA channels to external serial audio devices over MI2S.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif-cpu.c | 451 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 451 insertions(+)

diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
index 8eedbccd8805..a455c6ded243 100644
--- a/sound/soc/qcom/qaif-cpu.c
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -313,3 +313,471 @@ const struct snd_soc_dai_ops asoc_qcom_qaif_cif_dai_ops = {
 	.trigger	= qaif_cif_daiops_trigger,
 };
 EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cif_dai_ops);
+
+static int qaif_aif_cfg_cpu_init_bitfields(struct device *dev,
+					    struct regmap *map)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_aud_intfctl *aif_intfctl;
+
+	aif_intfctl = devm_kzalloc(dev, sizeof(struct qaif_aud_intfctl), GFP_KERNEL);
+	if (!aif_intfctl)
+		return -ENOMEM;
+
+	/*
+	 * Bulk-allocate all AIF intfctl fields in one call.
+	 * Order must match struct qaif_aud_intfctl member order:
+	 * inv_sync, sync_delay, sync_mode, sync_src,
+	 * slot_width_rx, slot_width_tx, sample_width_rx, sample_width_tx,
+	 * mono_mode_rx, mono_mode_tx,
+	 * lane_en, lane_dir, loopback_en, ctrl_data_oe,
+	 * slot_en_rx_mask, slot_en_tx_mask,
+	 * full_cycle_en, bits_per_lane,
+	 * enable, enable_tx, enable_rx,
+	 * reset, reset_tx, reset_rx
+	 */
+	{
+		const struct reg_field aif_intfctl_fields[] = {
+			v->aif_inv_sync,
+			v->aif_sync_delay,
+			v->aif_sync_mode,
+			v->aif_sync_src,
+			v->aif_slot_width_rx,
+			v->aif_slot_width_tx,
+			v->aif_sample_width_rx,
+			v->aif_sample_width_tx,
+			v->aif_mono_mode_rx,
+			v->aif_mono_mode_tx,
+			v->aif_lane_en,
+			v->aif_lane_dir,
+			v->aif_loopback_en,
+			v->aif_ctrl_data_oe,
+			v->aif_slot_en_rx_mask,
+			v->aif_slot_en_tx_mask,
+			v->aif_full_cycle_en,
+			v->aif_bits_per_lane,
+			v->aif_enable,
+			v->aif_enable_tx,
+			v->aif_enable_rx,
+			v->aif_reset,
+			v->aif_reset_tx,
+			v->aif_reset_rx,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&aif_intfctl->inv_sync,
+					aif_intfctl_fields,
+					ARRAY_SIZE(aif_intfctl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating AIF interface regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	drvdata->aif_intfctl = aif_intfctl;
+
+	return 0;
+}
+
+static int qaif_aif_cpu_init_bitfields(struct device *dev,
+			struct regmap *map)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_dmactl *rd_dmactl;
+	struct qaif_dmactl *wr_dmactl;
+
+	rd_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL);
+	if (!rd_dmactl)
+		return -ENOMEM;
+
+	wr_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL);
+	if (!wr_dmactl)
+		return -ENOMEM;
+
+	/*
+	 * Bulk-allocate AIF RDDMA dmactl fields.
+	 * Order must match struct qaif_dmactl member order:
+	 * enable, reset, num_ot, dma_dyncclk, burst16, burst8, burst4, burst2, burst1, shram_wm
+	 */
+	{
+		const struct reg_field aif_rd_dmactl_fields[] = {
+			v->rddma_enable,
+			v->rddma_reset,
+			v->rddma_num_ot,
+			v->rddma_dma_dyncclk,
+			v->rddma_burst16,
+			v->rddma_burst8,
+			v->rddma_burst4,
+			v->rddma_burst2,
+			v->rddma_burst1,
+			v->rddma_shram_wm,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&rd_dmactl->enable,
+					aif_rd_dmactl_fields,
+					ARRAY_SIZE(aif_rd_dmactl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating AIF RDDMA dmactl regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Bulk-allocate AIF WRDMA dmactl fields.
+	 * Order must match struct qaif_dmactl member order:
+	 * enable, reset, num_ot, dma_dyncclk, burst16, burst8, burst4, burst2, burst1, shram_wm
+	 */
+	{
+		const struct reg_field aif_wr_dmactl_fields[] = {
+			v->wrdma_enable,
+			v->wrdma_reset,
+			v->wrdma_num_ot,
+			v->wrdma_dma_dyncclk,
+			v->wrdma_burst16,
+			v->wrdma_burst8,
+			v->wrdma_burst4,
+			v->wrdma_burst2,
+			v->wrdma_burst1,
+			v->wrdma_shram_wm,
+		};
+		int ret = devm_regmap_field_bulk_alloc(dev, map,
+					&wr_dmactl->enable,
+					aif_wr_dmactl_fields,
+					ARRAY_SIZE(aif_wr_dmactl_fields));
+		if (ret) {
+			dev_err(dev, "error allocating AIF WRDMA dmactl regmap fields: %d\n", ret);
+			return ret;
+		}
+	}
+
+	drvdata->aif_rd_dmactl = rd_dmactl;
+	drvdata->aif_wr_dmactl = wr_dmactl;
+
+	return 0;
+}
+
+static int qaif_aif_cpu_daiops_startup(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	int idx, ret = 0;
+
+	idx = v->get_dma_idx(dai->driver->id);
+	if (idx < 0) {
+		dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	ret = clk_prepare(drvdata->mi2s_bit_clk[idx]);
+	if (ret) {
+		dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void qaif_aif_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
+					 struct snd_soc_dai *dai)
+{
+	struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_aud_intfctl *aif_intfctl = drvdata->aif_intfctl;
+	const struct qaif_aif_config *aif_intf_cfg;
+	int idx = v->get_dma_idx(dai->driver->id);
+
+	if (idx < 0) {
+		dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return;
+	}
+
+	aif_intf_cfg = &drvdata->aif_intf_cfg[idx];
+
+	if (aif_intf_cfg->loopback_en)
+		regmap_fields_write(aif_intfctl->enable, idx, QAIF_AIF_CTL_ENABLE_OFF);
+	else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		regmap_fields_write(aif_intfctl->enable_tx, idx, QAIF_AIF_CTL_ENABLE_OFF);
+	else
+		regmap_fields_write(aif_intfctl->enable_rx, idx, QAIF_AIF_CTL_ENABLE_OFF);
+
+	clk_unprepare(drvdata->mi2s_bit_clk[idx]);
+}
+
+static int qaif_aif_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
+				       struct snd_soc_dai *dai)
+{
+	struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	int idx = v->get_dma_idx(dai->driver->id);
+
+	if (idx < 0)
+		return 0;
+
+	clk_disable(drvdata->mi2s_bit_clk[idx]);
+	return 0;
+}
+
+static int qaif_aif_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *params,
+					  struct snd_soc_dai *dai)
+{
+	struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_aud_intfctl *aif_intfctl = drvdata->aif_intfctl;
+	const struct qaif_aif_config *aif_intf_cfg = NULL;
+	int idx;
+	snd_pcm_format_t format = params_format(params);
+	unsigned int channels = params_channels(params);
+	unsigned int rate = params_rate(params);
+	unsigned int slot_width = 32;
+	int bitwidth, ret;
+
+	if (!aif_intfctl) {
+		dev_err(dai->dev, "AIF interface control not initialized\n");
+		return -EINVAL;
+	}
+
+	idx = v->get_dma_idx(dai->driver->id);
+
+	if (idx < 0) {
+		dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	aif_intf_cfg = &drvdata->aif_intf_cfg[idx];
+
+	if (!aif_intf_cfg) {
+		dev_err(dai->dev, "AIF interface config not found\n");
+		return -EINVAL;
+	}
+	bitwidth = snd_pcm_format_width(format);
+	if (bitwidth < 0) {
+		dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth);
+		return bitwidth;
+	}
+
+	/* SYNC_CFG: write all four sync fields */
+	ret = regmap_fields_write(aif_intfctl->inv_sync, idx, aif_intf_cfg->invert_sync);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write inv_sync: %d\n", ret);
+		return ret;
+	}
+	ret = regmap_fields_write(aif_intfctl->sync_delay, idx, aif_intf_cfg->sync_delay);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write sync_delay: %d\n", ret);
+		return ret;
+	}
+	ret = regmap_fields_write(aif_intfctl->sync_mode, idx, aif_intf_cfg->sync_mode);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write sync_mode: %d\n", ret);
+		return ret;
+	}
+	ret = regmap_fields_write(aif_intfctl->sync_src, idx, aif_intf_cfg->sync_src);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write sync_src: %d\n", ret);
+		return ret;
+	}
+
+	/* LANE_CFG: write all four lane fields */
+	ret = regmap_fields_write(aif_intfctl->loopback_en, idx, aif_intf_cfg->loopback_en);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write loopback_en: %d\n", ret);
+		return ret;
+	}
+	ret = regmap_fields_write(aif_intfctl->ctrl_data_oe, idx, aif_intf_cfg->ctrl_data_oe);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write ctrl_data_oe: %d\n", ret);
+		return ret;
+	}
+	ret = regmap_fields_write(aif_intfctl->lane_en, idx, aif_intf_cfg->lane_en_mask);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write lane_en (mask=0x%02X): %d\n",
+			aif_intf_cfg->lane_en_mask, ret);
+		return ret;
+	}
+	ret = regmap_fields_write(aif_intfctl->lane_dir, idx, aif_intf_cfg->lane_dir_mask);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write lane_dir (mask=0x%02X): %d\n",
+			aif_intf_cfg->lane_dir_mask, ret);
+		return ret;
+	}
+
+	/* CFG: full_cycle_en */
+	ret = regmap_fields_write(aif_intfctl->full_cycle_en, idx, aif_intf_cfg->full_cycle_en);
+	if (ret) {
+		dev_err(dai->dev, "Failed to write full_cycle_en: %d\n", ret);
+		return ret;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		slot_width = aif_intf_cfg->slot_width_tx;
+		/* BIT_WIDTH_CFG: TX slot width and sample width */
+		ret = regmap_fields_write(aif_intfctl->slot_width_tx, idx,
+					  QAIF_AIF_SLOT_WIDTH(slot_width));
+		if (ret) {
+			dev_err(dai->dev, "Failed to write slot_width_tx: %d\n", ret);
+			return ret;
+		}
+		ret = regmap_fields_write(aif_intfctl->sample_width_tx, idx,
+					  QAIF_AIF_SAMPLE_WIDTH(bitwidth));
+		if (ret) {
+			dev_err(dai->dev, "Failed to write sample_width_tx: %d\n", ret);
+			return ret;
+		}
+
+		/* ACTV_SLOT_EN_TX */
+		ret = regmap_fields_write(aif_intfctl->slot_en_tx_mask, idx,
+					  aif_intf_cfg->slot_en_tx_mask);
+		if (ret) {
+			dev_err(dai->dev, "Failed to write slot_en_tx_mask (0x%08X): %d\n",
+				aif_intf_cfg->slot_en_tx_mask, ret);
+			return ret;
+		}
+
+		/* FRAME_CFG: bits_per_lane */
+		ret = regmap_fields_write(aif_intfctl->bits_per_lane, idx,
+					  (slot_width * aif_intf_cfg->bits_per_lane) - 1);
+		if (ret) {
+			dev_err(dai->dev, "Failed to write bits_per_lane: %d\n", ret);
+			return ret;
+		}
+
+		/* MI2S_CFG: TX mono mode */
+		ret = regmap_fields_write(aif_intfctl->mono_mode_tx, idx,
+					  (channels >= 2) ? QAIF_AUD_INTF_CTL_STEREO
+							  : QAIF_AUD_INTF_CTL_MONO);
+		if (ret) {
+			dev_err(dai->dev, "Failed to write mono_mode_tx: %d\n", ret);
+			return ret;
+		}
+	} else {
+		slot_width = aif_intf_cfg->slot_width_rx;
+		/* BIT_WIDTH_CFG: RX slot width and sample width */
+		ret = regmap_fields_write(aif_intfctl->slot_width_rx, idx,
+					  QAIF_AIF_SLOT_WIDTH(slot_width));
+		if (ret) {
+			dev_err(dai->dev, "Failed to write slot_width_rx: %d\n", ret);
+			return ret;
+		}
+		ret = regmap_fields_write(aif_intfctl->sample_width_rx, idx,
+					  QAIF_AIF_SAMPLE_WIDTH(bitwidth));
+		if (ret) {
+			dev_err(dai->dev, "Failed to write sample_width_rx: %d\n", ret);
+			return ret;
+		}
+
+		/* ACTV_SLOT_EN_RX */
+		ret = regmap_fields_write(aif_intfctl->slot_en_rx_mask, idx,
+					  aif_intf_cfg->slot_en_rx_mask);
+		if (ret) {
+			dev_err(dai->dev, "Failed to write slot_en_rx_mask (0x%08X): %d\n",
+				aif_intf_cfg->slot_en_rx_mask, ret);
+			return ret;
+		}
+
+		/* FRAME_CFG: bits_per_lane */
+		ret = regmap_fields_write(aif_intfctl->bits_per_lane, idx,
+					  (slot_width * aif_intf_cfg->bits_per_lane) - 1);
+		if (ret) {
+			dev_err(dai->dev, "Failed to write bits_per_lane: %d\n", ret);
+			return ret;
+		}
+
+		/* MI2S_CFG: RX mono mode */
+		ret = regmap_fields_write(aif_intfctl->mono_mode_rx, idx,
+					  (channels >= 2) ? QAIF_AUD_INTF_CTL_STEREO
+							  : QAIF_AUD_INTF_CTL_MONO);
+		if (ret) {
+			dev_err(dai->dev, "Failed to write mono_mode_rx: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = clk_set_rate(drvdata->mi2s_bit_clk[idx],
+			   rate * slot_width * aif_intf_cfg->bits_per_lane);
+	if (ret) {
+		dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
+			rate * slot_width * aif_intf_cfg->bits_per_lane, ret);
+		return ret;
+	}
+	dev_dbg(dai->dev, "setting IBIT clock to %u\n",
+		rate * slot_width * aif_intf_cfg->bits_per_lane);
+
+	ret = clk_enable(drvdata->mi2s_bit_clk[idx]);
+	if (ret) {
+		dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+		return ret;
+	}
+	snd_soc_dai_set_tdm_slot(codec_dai, 0x0f, 0b11, aif_intf_cfg->bits_per_lane, slot_width);
+	snd_soc_dai_set_sysclk(codec_dai, 0, rate * aif_intf_cfg->bits_per_lane * slot_width, 0);
+
+	return 0;
+}
+
+static int qaif_aif_cpu_daiops_trigger(struct snd_pcm_substream *substream,
+				       int cmd, struct snd_soc_dai *dai)
+{
+	struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	const struct qaif_variant *v = drvdata->variant;
+	int idx, ret = -EINVAL;
+	const struct qaif_aif_config *aif_intf_cfg;
+
+	idx = v->get_dma_idx(dai->driver->id);
+	if (idx < 0) {
+		dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	aif_intf_cfg = &drvdata->aif_intf_cfg[idx];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (aif_intf_cfg->loopback_en)
+			ret = regmap_fields_write(drvdata->aif_intfctl->enable, idx, QAIF_AIF_CTL_ENABLE_ON);
+		else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = regmap_fields_write(drvdata->aif_intfctl->enable_tx, idx, QAIF_AIF_CTL_ENABLE_ON);
+		else
+			ret = regmap_fields_write(drvdata->aif_intfctl->enable_rx, idx, QAIF_AIF_CTL_ENABLE_ON);
+		if (ret)
+			dev_err(dai->dev, "error writing to AIF CTL reg: %d\n", ret);
+
+		ret = clk_enable(drvdata->mi2s_bit_clk[idx]);
+		if (ret) {
+			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+			return ret;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+
+		if (aif_intf_cfg->loopback_en)
+			ret = regmap_fields_write(drvdata->aif_intfctl->enable, idx, QAIF_AIF_CTL_ENABLE_OFF);
+		else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = regmap_fields_write(drvdata->aif_intfctl->enable_tx, idx, QAIF_AIF_CTL_ENABLE_OFF);
+		else
+			ret = regmap_fields_write(drvdata->aif_intfctl->enable_rx, idx, QAIF_AIF_CTL_ENABLE_OFF);
+		if (ret)
+			dev_err(dai->dev, "error writing to AIF CTL reg: %d\n", ret);
+
+		clk_disable(drvdata->mi2s_bit_clk[idx]);
+
+		break;
+	}
+
+	return ret;
+}
+
+const struct snd_soc_dai_ops asoc_qcom_qaif_aif_cpu_dai_ops = {
+	.startup	= qaif_aif_cpu_daiops_startup,
+	.shutdown	= qaif_aif_cpu_daiops_shutdown,
+	.hw_free	= qaif_aif_cpu_daiops_hw_free,
+	.hw_params	= qaif_aif_cpu_daiops_hw_params,
+	.trigger	= qaif_aif_cpu_daiops_trigger,
+};
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_aif_cpu_dai_ops);
-- 
2.34.1


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

* [PATCH 8/13] ASoC: qcom: Add generic of_xlate_dai_name helper to common
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (6 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 7/13] ASoC: qcom: Add QAIF AIF " Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:48   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 9/13] ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper Harendra Gautam
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Multiple Qualcomm ASoC CPU DAI drivers need to resolve a sound-dai
phandle argument to a DAI name by searching the component's DAI driver
array by ID. Each driver currently implements this identically.

Extract the common logic into asoc_qcom_of_xlate_dai_name() in
common.c so it can be shared across drivers without duplication.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/common.c | 34 ++++++++++++++++++++++++++++++++++
 sound/soc/qcom/common.h |  5 +++++
 2 files changed, 39 insertions(+)

diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index cf1f3a767cee..46569290d44c 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -23,6 +23,40 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = {
 	SND_SOC_DAPM_SPK("DP7 Jack", NULL),
 };
 
+/**
+ * asoc_qcom_of_xlate_dai_name - Resolve a sound-dai phandle argument to a
+ *                               DAI name by searching the DAI driver array.
+ * @dai_drv:  Array of DAI drivers registered by the component.
+ * @num_dai:  Number of entries in @dai_drv.
+ * @args:     Phandle arguments from the sound-dai property; args[0] is the
+ *            DAI ID.
+ * @dai_name: Output pointer set to the matched DAI name on success.
+ *
+ * Returns 0 on success, -EINVAL if args_count != 1 or no match is found.
+ */
+int asoc_qcom_of_xlate_dai_name(const struct snd_soc_dai_driver *dai_drv,
+				int num_dai,
+				const struct of_phandle_args *args,
+				const char **dai_name)
+{
+	int id, i;
+
+	if (args->args_count != 1)
+		return -EINVAL;
+
+	id = args->args[0];
+
+	for (i = 0; i < num_dai; i++) {
+		if (dai_drv[i].id == id) {
+			*dai_name = dai_drv[i].name;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_of_xlate_dai_name);
+
 int qcom_snd_parse_of(struct snd_soc_card *card)
 {
 	struct device_node *np;
diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
index ee6662885593..5baf51a39c97 100644
--- a/sound/soc/qcom/common.h
+++ b/sound/soc/qcom/common.h
@@ -6,6 +6,7 @@
 
 #include <dt-bindings/sound/qcom,q6afe.h>
 #include <sound/soc.h>
+#include <sound/soc-dai.h>
 
 #define LPASS_MAX_PORT			(SENARY_MI2S_TX + 1)
 
@@ -15,5 +16,9 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
 int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
 			   struct snd_soc_jack *dp_jack, int id);
 
+int asoc_qcom_of_xlate_dai_name(const struct snd_soc_dai_driver *dai_drv,
+				int num_dai,
+				const struct of_phandle_args *args,
+				const char **dai_name);
 
 #endif
-- 
2.34.1


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

* [PATCH 9/13] ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (7 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 8/13] ASoC: qcom: Add generic of_xlate_dai_name helper to common Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:51   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Replace the private static asoc_qcom_of_xlate_dai_name() implementation
in lpass-cpu.c with a thin wrapper that calls the new shared helper
from common.c. This removes the duplicate implementation.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/lpass-cpu.c | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)

diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 242bc16da36d..35c2c8030024 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -18,6 +18,7 @@
 #include <sound/soc-dai.h>
 #include "lpass-lpaif-reg.h"
 #include "lpass.h"
+#include "common.h"
 
 #define LPASS_CPU_MAX_MI2S_LINES	4
 #define LPASS_CPU_I2S_SD0_MASK		BIT(0)
@@ -458,30 +459,20 @@ const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2 = {
 };
 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops2);
 
-static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component,
+static int lpass_cpu_of_xlate_dai_name(struct snd_soc_component *component,
 				   const struct of_phandle_args *args,
 				   const char **dai_name)
 {
 	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
 	const struct lpass_variant *variant = drvdata->variant;
-	int id = args->args[0];
-	int ret = -EINVAL;
-	int i;
 
-	for (i = 0; i  < variant->num_dai; i++) {
-		if (variant->dai_driver[i].id == id) {
-			*dai_name = variant->dai_driver[i].name;
-			ret = 0;
-			break;
-		}
-	}
-
-	return ret;
+	return asoc_qcom_of_xlate_dai_name(variant->dai_driver,
+					   variant->num_dai, args, dai_name);
 }
 
 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 	.name = "lpass-cpu",
-	.of_xlate_dai_name = asoc_qcom_of_xlate_dai_name,
+	.of_xlate_dai_name = lpass_cpu_of_xlate_dai_name,
 	.legacy_dai_naming = 1,
 };
 
-- 
2.34.1


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

* [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (8 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 9/13] ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 11:02   ` Krzysztof Kozlowski
  2026-06-05 11:15   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 11/13] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
                   ` (2 subsequent siblings)
  12 siblings, 2 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add the core CPU and platform bring-up needed for the Qualcomm Audio
Interface driver.

The existing QAIF DAI code needs device-level infrastructure before it can
configure interfaces or run PCM streams. Add the MMIO regmap setup,
including readable, writable and volatile register tables, so the driver
can safely access QAIF control, DMA, interrupt and SHRAM registers.

Parse the QAIF AIF child nodes from devicetree and store the per-interface
PCM, TDM or MI2S configuration for use when programming the AUD_INTF
registers. Add the platform probe, remove and shutdown entry points to map
the device registers, initialize variant-specific resources, allocate
regmap fields, acquire clocks and register the CPU DAI component.

Add the QAIF PCM platform support needed for DMA streams, including PCM
hardware constraints, coherent DMA buffer allocation using
dma_alloc_coherent(), EE resource mapping, QXM DMA routing and SHRAM
partitioning for AIF and CIF DMA channels.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif-cpu.c      | 808 +++++++++++++++++++++++++++++++++
 sound/soc/qcom/qaif-platform.c | 276 +++++++++++
 2 files changed, 1084 insertions(+)
 create mode 100644 sound/soc/qcom/qaif-platform.c

diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
index a455c6ded243..62a78b30744c 100644
--- a/sound/soc/qcom/qaif-cpu.c
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -779,3 +779,804 @@ const struct snd_soc_dai_ops asoc_qcom_qaif_aif_cpu_dai_ops = {
 	.trigger	= qaif_aif_cpu_daiops_trigger,
 };
 EXPORT_SYMBOL_GPL(asoc_qcom_qaif_aif_cpu_dai_ops);
+
+static int qaif_cpu_of_xlate_dai_name(struct snd_soc_component *component,
+				       const struct of_phandle_args *args,
+				       const char **dai_name)
+{
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+
+	return asoc_qcom_of_xlate_dai_name(v->dai_driver,
+					   v->num_dai, args, dai_name);
+}
+
+static const struct snd_soc_component_driver qaif_cpu_comp_driver = {
+	.name = "qaif-cpu",
+	.of_xlate_dai_name = qaif_cpu_of_xlate_dai_name,
+};
+
+static bool audio_qaif_regmap_writeable(struct device *dev, unsigned int reg)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+	const struct qaif_variant *v = drvdata->variant;
+	int i;
+
+	/* EE maps */
+	if (reg == QAIF_EE_RDDMA_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_WRDMA_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_INTF_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_CODEC_RDDMA_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_CODEC_WRDMA_MAP_REG(v))
+		return true;
+
+	/* QXM DMA path mapping */
+	if (reg == QAIF_RDDMA_MAP_QXM)
+		return true;
+	if (reg == QAIF_WRDMA_MAP_QXM)
+		return true;
+	if (reg == QAIF_CODEC_RDDMA_MAP_QXM)
+		return true;
+	if (reg == QAIF_CODEC_WRDMA_MAP_QXM)
+		return true;
+
+	/* SID maps */
+	if (reg == QAIF_WRDMA_SID_MAP_REG)
+		return true;
+	if (reg == QAIF_CODEC_WRDMA_SID_MAP_REG)
+		return true;
+	if (reg == QAIF_RDDMA_SID_MAP_REG)
+		return true;
+	if (reg == QAIF_CODEC_RDDMA_SID_MAP_REG)
+		return true;
+
+	/* SHRAM QXM0 start address and length */
+	for (i = 0; i < v->num_rddma; i++) {
+		if (reg == QAIF_RDDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_RDDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+	for (i = 0; i < v->num_codec_rddma; i++) {
+		if (reg == QAIF_CODEC_RDDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+	for (i = 0; i < v->num_wrdma; i++) {
+		if (reg == QAIF_WRDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_WRDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+	for (i = 0; i < v->num_codec_wrdma; i++) {
+		if (reg == QAIF_CODEC_WRDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+
+	/* EE IRQ EN and CLR */
+	for (i = 0; i < DMA_TYPE_MAX; i++) {
+		if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, i))
+			return true;
+	}
+
+	/* AUD_INTF control and configuration */
+	for (i = 0; i < v->num_intf; i++) {
+		if (reg == QAIF_AUD_INTF_CTL_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_SYNC_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_FRAME_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_ACTV_SLOT_EN_TX_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_ACTV_SLOT_EN_RX_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_LANE_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_MI2S_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_CFG_REG(i))
+			return true;
+	}
+
+	/* RDDMA control and configuration */
+	for (i = 0; i < v->num_rddma; i++) {
+		if (reg == QAIF_RDDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_PERIOD_LEN_REG(v, i))
+			return true;
+	}
+
+	/* CODEC RDDMA control and configuration */
+	for (i = 0; i < v->num_codec_rddma; i++) {
+		if (reg == QAIF_CODEC_RDDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_INTF_CFG_REG(v, i))
+			return true;
+	}
+
+	/* WRDMA control and configuration */
+	for (i = 0; i < v->num_wrdma; i++) {
+		if (reg == QAIF_WRDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_PERIOD_LEN_REG(v, i))
+			return true;
+	}
+
+	/* CODEC WRDMA control and configuration */
+	for (i = 0; i < v->num_codec_wrdma; i++) {
+		if (reg == QAIF_CODEC_WRDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_INTF_CFG_REG(v, i))
+			return true;
+	}
+
+	return false;
+}
+
+static bool audio_qaif_regmap_readable(struct device *dev, unsigned int reg)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+	const struct qaif_variant *v = drvdata->variant;
+	int i;
+
+	/* Summary IRQ status */
+	if (reg == QAIF_SUMMARY_IRQSTAT_REG(v))
+		return true;
+
+	/* EE maps */
+	if (reg == QAIF_EE_RDDMA_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_WRDMA_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_INTF_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_CODEC_RDDMA_MAP_REG(v))
+		return true;
+	if (reg == QAIF_EE_CODEC_WRDMA_MAP_REG(v))
+		return true;
+
+	/* QXM DMA path mapping */
+	if (reg == QAIF_RDDMA_MAP_QXM)
+		return true;
+	if (reg == QAIF_WRDMA_MAP_QXM)
+		return true;
+	if (reg == QAIF_CODEC_RDDMA_MAP_QXM)
+		return true;
+	if (reg == QAIF_CODEC_WRDMA_MAP_QXM)
+		return true;
+
+	/* SID maps */
+	if (reg == QAIF_WRDMA_SID_MAP_REG)
+		return true;
+	if (reg == QAIF_CODEC_WRDMA_SID_MAP_REG)
+		return true;
+	if (reg == QAIF_RDDMA_SID_MAP_REG)
+		return true;
+	if (reg == QAIF_CODEC_RDDMA_SID_MAP_REG)
+		return true;
+
+	/* SHRAM QXM0 start address and length */
+	for (i = 0; i < v->num_rddma; i++) {
+		if (reg == QAIF_RDDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_RDDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+	for (i = 0; i < v->num_codec_rddma; i++) {
+		if (reg == QAIF_CODEC_RDDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+	for (i = 0; i < v->num_wrdma; i++) {
+		if (reg == QAIF_WRDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_WRDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+	for (i = 0; i < v->num_codec_wrdma; i++) {
+		if (reg == QAIF_CODEC_WRDMA_QXM0_SHRAM_ST_ADDR(i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_QXM0_SHRAM_LEN(i))
+			return true;
+	}
+
+	/* EE IRQ EN, CLR and STATUS */
+	for (i = 0; i < DMA_TYPE_MAX; i++) {
+		if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, i))
+			return true;
+	}
+
+	/* AUD_INTF control and configuration */
+	for (i = 0; i < v->num_intf; i++) {
+		if (reg == QAIF_AUD_INTF_CTL_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_SYNC_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_FRAME_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_ACTV_SLOT_EN_TX_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_ACTV_SLOT_EN_RX_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_LANE_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_MI2S_CFG_REG(i))
+			return true;
+		if (reg == QAIF_AUD_INTF_CFG_REG(i))
+			return true;
+	}
+
+	/* RDDMA control, configuration and current address */
+	for (i = 0; i < v->num_rddma; i++) {
+		if (reg == QAIF_RDDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_CURR_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_RDDMA_PERIOD_LEN_REG(v, i))
+			return true;
+	}
+
+	/* CODEC RDDMA control, configuration and current address */
+	for (i = 0; i < v->num_codec_rddma; i++) {
+		if (reg == QAIF_CODEC_RDDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_RDDMA_INTF_CFG_REG(v, i))
+			return true;
+	}
+
+	/* WRDMA control, configuration and current address */
+	for (i = 0; i < v->num_wrdma; i++) {
+		if (reg == QAIF_WRDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_CURR_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_WRDMA_PERIOD_LEN_REG(v, i))
+			return true;
+	}
+
+	/* CODEC WRDMA control, configuration and current address */
+	for (i = 0; i < v->num_codec_wrdma; i++) {
+		if (reg == QAIF_CODEC_WRDMA_CTL_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_CFG_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, i))
+			return true;
+		if (reg == QAIF_CODEC_WRDMA_INTF_CFG_REG(v, i))
+			return true;
+	}
+
+	return false;
+}
+
+static bool audio_qaif_regmap_volatile(struct device *dev, unsigned int reg)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+	const struct qaif_variant *v = drvdata->variant;
+	int i;
+
+	/* Summary IRQ status - hardware updated on any interrupt */
+	if (reg == QAIF_SUMMARY_IRQSTAT_REG(v))
+		return true;
+
+	/* EE IRQ status - hardware updated on interrupt */
+	for (i = 0; i < DMA_TYPE_MAX; i++) {
+		if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, i))
+			return true;
+		if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, i))
+			return true;
+	}
+
+	/* DMA current address - hardware updated during streaming */
+	for (i = 0; i < v->num_rddma; i++) {
+		if (reg == QAIF_RDDMA_CURR_ADDR_REG(v, i))
+			return true;
+	}
+	for (i = 0; i < v->num_wrdma; i++) {
+		if (reg == QAIF_WRDMA_CURR_ADDR_REG(v, i))
+			return true;
+	}
+	for (i = 0; i < v->num_codec_rddma; i++) {
+		if (reg == QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, i))
+			return true;
+	}
+	for (i = 0; i < v->num_codec_wrdma; i++) {
+		if (reg == QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, i))
+			return true;
+	}
+
+	return false;
+}
+
+static struct regmap_config audio_qaif_regmap_config = {
+	.name = "audio_qaif_cpu",
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.writeable_reg = audio_qaif_regmap_writeable,
+	.readable_reg = audio_qaif_regmap_readable,
+	.volatile_reg = audio_qaif_regmap_volatile,
+	.cache_type = REGCACHE_FLAT,
+};
+
+static int of_qaif_parse_aif_intf_cfg(struct device *dev,
+					struct qaif_drv_data *data)
+{
+	const struct qaif_variant *v = data->variant;
+	struct device_node *np = dev->of_node;
+	struct device_node *intf_np;
+	struct qaif_aif_config *cfg;
+	const __be32 *lane_cfg_prop;
+	int ret, j;
+	int lane_cfg_len;
+	u32 dai_id, intf_idx;
+	int num_interfaces = 0;
+
+	if (!v) {
+		dev_err(dev, "No variant data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Iterate over all child nodes of qaif_cpu and process only those
+	 * with a recognised AIF interface compatible. The compatible string
+	 * identifies the serial protocol the interface is wired for on the
+	 * board: qcom,qaif-pcm-dai, qcom,qaif-tdm-dai or qcom,qaif-mi2s-dai.
+	 * Other child nodes are silently skipped.
+	 */
+	for_each_child_of_node(np, intf_np) {
+		enum qaif_aif_mode mode;
+
+		if (of_device_is_compatible(intf_np, "qcom,qaif-pcm-dai"))
+			mode = QAIF_AIF_MODE_PCM;
+		else if (of_device_is_compatible(intf_np, "qcom,qaif-tdm-dai"))
+			mode = QAIF_AIF_MODE_TDM;
+		else if (of_device_is_compatible(intf_np, "qcom,qaif-mi2s-dai"))
+			mode = QAIF_AIF_MODE_MI2S;
+		else
+			continue;
+
+		if (num_interfaces >= QAIF_MAX_AIF_CFG_CNT) {
+			dev_warn(dev, "Too many AIF interfaces, limiting to %d\n",
+				 QAIF_MAX_AIF_CFG_CNT);
+			of_node_put(intf_np);
+			break;
+		}
+
+
+		ret = of_property_read_u32(intf_np, "reg", &dai_id);
+		if (ret) {
+			dev_err(dev, "Missing reg for interface %d: %s\n", num_interfaces, intf_np->name);
+			continue;
+		}
+
+		if (v->get_dma_idx) {
+			intf_idx = v->get_dma_idx(dai_id);
+			if (intf_idx < 0) {
+				dev_err(dev, "Invalid DAI ID %d for interface '%s' (node %d)\n",
+					dai_id, intf_np->name, num_interfaces);
+				continue;
+			}
+			if (intf_idx >= ARRAY_SIZE(data->aif_intf_cfg)) {
+				dev_err(dev, "DAI ID %d maps to out-of-range intf_idx %d\n",
+					dai_id, intf_idx);
+				continue;
+			}
+		} else {
+			dev_err(dev, "can not get intf idx for : %d: %s\n", num_interfaces, intf_np->name);
+			of_node_put(intf_np);
+			return -EINVAL;
+		}
+		cfg = &data->aif_intf_cfg[intf_idx];
+		cfg->mode = mode;
+
+		/* Parse sync configuration — mode-specific */
+		if (mode == QAIF_AIF_MODE_MI2S) {
+			/* MI2S: sync mode is fixed (long sync = 1, WS-based) */
+			cfg->sync_mode = 1;
+		} else {
+			/* PCM/TDM: sync mode comes from DT (0=short, 1=long) */
+			ret = of_property_read_u32(intf_np, "qcom,qaif-aif-sync-mode",
+						   &cfg->sync_mode);
+			if (ret) {
+				dev_err(dev, "Missing sync-mode for interface %d\n",
+					num_interfaces);
+				of_node_put(intf_np);
+				return -EINVAL;
+			}
+		}
+
+		ret = of_property_read_u32(intf_np, "qcom,qaif-aif-sync-src", &cfg->sync_src);
+		if (ret) {
+			dev_warn(dev, "Missing sync-src for interface %d\n", num_interfaces);
+			cfg->sync_src = 0;
+		}
+
+		cfg->invert_sync = of_property_read_bool(intf_np, "qcom,qaif-aif-invert-sync");
+
+		ret = of_property_read_u32(intf_np, "qcom,qaif-aif-sync-delay", &cfg->sync_delay);
+		if (ret) {
+			dev_warn(dev, "Missing sync-delay for interface %d\n", num_interfaces);
+			cfg->sync_delay = 0;
+		}
+
+		/* Parse slot and sample width configuration */
+		ret = of_property_read_u32(intf_np, "qcom,qaif-aif-slot-width-rx", &cfg->slot_width_rx);
+		if (ret) {
+			dev_warn(dev, "Missing slot-width-rx for interface %d\n", num_interfaces);
+			cfg->slot_width_rx = 0;
+		}
+
+		ret = of_property_read_u32(intf_np, "qcom,qaif-aif-slot-width-tx", &cfg->slot_width_tx);
+		if (ret) {
+			dev_warn(dev, "Missing slot-width-tx for interface %d\n", num_interfaces);
+			cfg->slot_width_tx = 0;
+		}
+
+		/* Parse slot enable masks — mode-specific */
+		if (mode == QAIF_AIF_MODE_MI2S) {
+			/* MI2S: always 2 active slots (left + right) */
+			cfg->slot_en_rx_mask = 0x3;
+			cfg->slot_en_tx_mask = 0x3;
+		} else {
+			/* PCM/TDM: active slot mask comes from DT */
+			ret = of_property_read_u32(intf_np, "qcom,qaif-aif-slot-en-rx-mask",
+						   &cfg->slot_en_rx_mask);
+			if (ret) {
+				dev_warn(dev, "Missing slot-en-rx-mask for interface %d\n",
+					 num_interfaces);
+				cfg->slot_en_rx_mask = 0;
+			}
+			ret = of_property_read_u32(intf_np, "qcom,qaif-aif-slot-en-tx-mask",
+						   &cfg->slot_en_tx_mask);
+			if (ret) {
+				dev_warn(dev, "Missing slot-en-tx-mask for interface %d\n",
+					 num_interfaces);
+				cfg->slot_en_tx_mask = 0;
+			}
+		}
+
+		/* Parse control configuration */
+		cfg->loopback_en = of_property_read_bool(intf_np, "qcom,qaif-aif-loopback");
+
+		cfg->ctrl_data_oe = of_property_read_bool(intf_np, "qcom,qaif-aif-ctrl-data-oe");
+
+		/* Parse lane configuration */
+		lane_cfg_prop = of_get_property(intf_np, "qcom,qaif-aif-lane-config", &lane_cfg_len);
+		if (lane_cfg_prop) {
+			/* Each lane config has 2 u32 values: enable and direction */
+			cfg->num_lanes = lane_cfg_len / (2 * sizeof(u32));
+			if (cfg->num_lanes > QAIF_MAX_LANES) {
+				dev_warn(dev, "Too many lanes (%d), limiting to %d\n",
+					 cfg->num_lanes, QAIF_MAX_LANES);
+				cfg->num_lanes = QAIF_MAX_LANES;
+			}
+
+			for (j = 0; j < cfg->num_lanes; j++) {
+				cfg->lane_cfg[j].enable = be32_to_cpup(lane_cfg_prop + (j * 2));
+				if (cfg->lane_cfg[j].enable)
+					cfg->lane_en_mask |= BIT(j);
+
+				cfg->lane_cfg[j].direction = be32_to_cpup(lane_cfg_prop + (j * 2 + 1));
+				if (cfg->lane_cfg[j].direction)
+					cfg->lane_dir_mask |= BIT(j);
+			}
+
+		} else {
+			dev_warn(dev, "Missing lane-config for interface %d\n", num_interfaces);
+			cfg->num_lanes = 0;
+		}
+
+		/* Mono/stereo mode is written directly from params_channels() in hw_params */
+
+		/* Parse frame configuration */
+		cfg->full_cycle_en = of_property_read_bool(intf_np, "qcom,qaif-aif-full-cycle-en");
+
+		ret = of_property_read_u32(intf_np, "qcom,qaif-aif-bits-per-lane", &cfg->bits_per_lane);
+		if (ret) {
+			dev_warn(dev, "Missing bits-per-lane for interface %d\n", num_interfaces);
+			cfg->bits_per_lane = 0;
+		}
+
+		num_interfaces++;
+	}
+
+	if (num_interfaces == 0) {
+		dev_err(dev, "No AIF child nodes with qcom,qaif-{pcm,tdm,mi2s}-dai compatible found\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int of_qaif_cdc_dma_clks_parse(struct device *dev,
+					struct qaif_drv_data *data)
+{
+	data->aud_dma_clk = devm_clk_get(dev, "aud_dma_clk");
+	if (IS_ERR(data->aud_dma_clk))
+		return PTR_ERR(data->aud_dma_clk);
+
+	data->aud_dma_mem_clk = devm_clk_get(dev, "aud_dma_mem_clk");
+	if (IS_ERR(data->aud_dma_mem_clk))
+		return PTR_ERR(data->aud_dma_mem_clk);
+
+	return 0;
+}
+
+int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata;
+	struct resource *res;
+	const struct qaif_variant *variant;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	int ret, i, dai_id, idx;
+	bool variant_init_done = false;
+
+	dev_dbg(dev, "%s\n", __func__);
+	drvdata = devm_kzalloc(dev, sizeof(struct qaif_drv_data), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drvdata);
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data)
+		return -EINVAL;
+
+	drvdata->variant = (const struct qaif_variant *)match->data;
+	variant = drvdata->variant;
+	if (!variant) {
+		dev_err(dev, "No variant data\n");
+		return -EINVAL;
+	}
+
+	ret = of_qaif_parse_aif_intf_cfg(dev, drvdata);
+	if (ret) {
+		dev_err(dev, "Failed to parse aif interfaces: %d\n", ret);
+		return -EINVAL;
+	}
+
+	drvdata->audio_qaif =
+			devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(drvdata->audio_qaif))
+		return PTR_ERR(drvdata->audio_qaif);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	audio_qaif_regmap_config.max_register = resource_size(res);
+
+	drvdata->audio_qaif_map = devm_regmap_init_mmio(dev, drvdata->audio_qaif,
+				&audio_qaif_regmap_config);
+	if (IS_ERR(drvdata->audio_qaif_map))
+		return PTR_ERR(drvdata->audio_qaif_map);
+
+	ret = of_qaif_cdc_dma_clks_parse(dev, drvdata);
+	if (ret) {
+		dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
+		return ret;
+	}
+
+	if (variant->init) {
+		ret = variant->init(pdev);
+		if (ret) {
+			dev_err(dev, "error initializing variant: %d\n", ret);
+			return ret;
+		}
+		variant_init_done = true;
+	}
+
+	for (i = 0; i < variant->num_dai; i++) {
+		dai_id = variant->dai_driver[i].id;
+		if (is_cif_dma_port(dai_id))
+			continue;
+		idx = variant->get_dma_idx(dai_id);
+		if (idx < 0)
+			continue;
+
+		drvdata->mi2s_bit_clk[idx] = devm_clk_get(dev,
+						variant->dai_bit_clk_names[idx]);
+		if (IS_ERR(drvdata->mi2s_bit_clk[idx])) {
+			dev_err(dev,
+				"error getting %s: %ld\n",
+				variant->dai_bit_clk_names[idx],
+				PTR_ERR(drvdata->mi2s_bit_clk[idx]));
+			ret = PTR_ERR(drvdata->mi2s_bit_clk[idx]);
+			goto err;
+		}
+	}
+
+	ret = qaif_aif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
+	if (ret) {
+		dev_err(dev, "error init cif bitfield: %d\n", ret);
+		goto err;
+	}
+
+	ret = qaif_aif_cfg_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
+	if (ret) {
+		dev_err(dev, "error init aif_intfctl field: %d\n", ret);
+		goto err;
+	}
+
+	ret = qaif_cif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
+	if (ret) {
+		dev_err(dev, "error init cif bitfield: %d\n", ret);
+		goto err;
+	}
+
+	ret = devm_snd_soc_register_component(dev, &qaif_cpu_comp_driver,
+								variant->dai_driver,
+								variant->num_dai);
+	if (ret) {
+		dev_err(dev, "error registering cpu driver: %d\n", ret);
+		goto err;
+	}
+
+	ret = asoc_qcom_qaif_platform_register(pdev);
+	if (ret) {
+		dev_err(dev, "error registering platform driver: %d\n", ret);
+		goto err;
+	}
+	dev_dbg(&pdev->dev, "%s: QAIF CPU-Platform Driver Registered Successfully\n", __func__);
+err:
+	if (ret && variant_init_done && variant->exit)
+		variant->exit(pdev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_probe);
+
+void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+
+	if (drvdata->variant->exit)
+		drvdata->variant->exit(pdev);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_remove);
+
+void asoc_qcom_qaif_cpu_platform_shutdown(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+
+	if (drvdata->variant->exit)
+		drvdata->variant->exit(pdev);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_shutdown);
+
+MODULE_DESCRIPTION("Qualcomm Audio Interface (QAIF) CPU DAI driver");
+MODULE_AUTHOR("Harendra Gautam <harendra.gautam@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
new file mode 100644
index 000000000000..14e3379ca560
--- /dev/null
+++ b/sound/soc/qcom/qaif-platform.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * qaif-platform.c -- ALSA SoC PCM platform driver for the Qualcomm Audio Interface (QAIF)
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "qaif-reg.h"
+#include "qaif.h"
+
+#define DRV_NAME "qaif-platform"
+
+/* 20 ms period at 48 kHz S16 stereo = 3840 bytes */
+#define QAIF_PLATFORM_BUFFER_MIN_SIZE		(960 * 2 * 2)
+#define QAIF_PLATFORM_PERIOD_BYTES_MIN		(960 * 2 * 2)
+#define QAIF_PLATFORM_BUFFER_SIZE			(4 * QAIF_PLATFORM_BUFFER_MIN_SIZE)
+#define QAIF_PLATFORM_PERIODS_MIN			2
+#define QAIF_PLATFORM_PERIODS_MAX			4
+
+
+static const struct snd_pcm_hardware qaif_platform_aif_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	=	QAIF_PLATFORM_BUFFER_SIZE,
+	.period_bytes_min	=	QAIF_PLATFORM_PERIOD_BYTES_MIN,
+	.period_bytes_max	=	QAIF_PLATFORM_BUFFER_SIZE / QAIF_PLATFORM_PERIODS_MIN,
+	.periods_min		=	QAIF_PLATFORM_PERIODS_MIN,
+	.periods_max		=	QAIF_PLATFORM_PERIODS_MAX,
+	.fifo_size		=	0,
+};
+
+static const struct snd_pcm_hardware qaif_platform_cif_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	=	QAIF_PLATFORM_BUFFER_SIZE,
+	.period_bytes_min	=	QAIF_PLATFORM_PERIOD_BYTES_MIN,
+	.period_bytes_max	=	QAIF_PLATFORM_BUFFER_SIZE / QAIF_PLATFORM_PERIODS_MIN,
+	.periods_min		=	QAIF_PLATFORM_PERIODS_MIN,
+	.periods_max		=	QAIF_PLATFORM_PERIODS_MAX,
+	.fifo_size		=	0,
+};
+
+static struct qaif_dma_mem_info *qaif_mem_alloc_attach(
+			struct snd_soc_component *component, size_t alloc_size)
+{
+	struct device *dev = component->dev;
+	struct qaif_dma_mem_info *dma_mem_info;
+
+	dma_mem_info = kzalloc(sizeof(*dma_mem_info), GFP_KERNEL);
+	if (!dma_mem_info)
+		return NULL;
+
+	dma_mem_info->alloc_size = alloc_size;
+
+	dma_mem_info->vaddr = dma_alloc_coherent(dev, alloc_size,
+						 &dma_mem_info->dma_addr,
+						 GFP_KERNEL);
+	if (!dma_mem_info->vaddr) {
+		dev_err(dev, "dma_alloc_coherent failed for %zu bytes\n", alloc_size);
+		kfree(dma_mem_info);
+		return NULL;
+	}
+
+	dev_dbg(dev, "%s: dma_addr=%pad vaddr=%p\n", __func__,
+			&dma_mem_info->dma_addr, dma_mem_info->vaddr);
+	return dma_mem_info;
+}
+
+static void qaif_mem_dealloc_detach(struct device *dev,
+				    struct qaif_dma_mem_info *dma_info)
+{
+	if (!dma_info)
+		return;
+
+	if (dma_info->vaddr)
+		dma_free_coherent(dev, dma_info->alloc_size,
+				  dma_info->vaddr, dma_info->dma_addr);
+
+	kfree(dma_info);
+}
+
+static struct qaif_dmactl *qaif_get_dmactl_handle(const struct snd_pcm_substream *substream,
+						      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 qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	struct qaif_dmactl *dmactl = NULL;
+
+	switch (cpu_dai->driver->id) {
+	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			dmactl = drvdata->aif_rd_dmactl;
+		else
+			dmactl = drvdata->aif_wr_dmactl;
+		break;
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+		dmactl = drvdata->cif_rd_dmactl;
+		break;
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		dmactl = drvdata->cif_wr_dmactl;
+		break;
+	}
+
+	return dmactl;
+}
+
+
+static int qaif_map_ee_resource(struct qaif_drv_data *drvdata)
+{
+	const struct qaif_variant *v = drvdata->variant;
+	struct regmap *map = drvdata->audio_qaif_map;
+	int ret = 0;
+	u32 mask;
+
+	mask = GENMASK(v->num_rddma - 1, 0);
+	ret |= regmap_write(map, QAIF_EE_RDDMA_MAP_REG(v), mask);
+
+	mask = GENMASK(v->num_wrdma - 1, 0);
+	ret |= regmap_write(map, QAIF_EE_WRDMA_MAP_REG(v), mask);
+
+	if (v->num_intf > 0) {
+		mask = GENMASK(v->num_intf - 1, 0);
+		ret |= regmap_write(map, QAIF_EE_INTF_MAP_REG(v), mask);
+	}
+
+	mask = GENMASK(v->num_codec_rddma - 1, 0);
+	ret |= regmap_write(map, QAIF_EE_CODEC_RDDMA_MAP_REG(v), mask);
+
+	mask = GENMASK(v->num_codec_wrdma - 1, 0);
+	ret |= regmap_write(map, QAIF_EE_CODEC_WRDMA_MAP_REG(v), mask);
+
+	if (ret)
+		return ret;
+	return 0;
+}
+
+static int qaif_map_dma_path(struct qaif_drv_data *drvdata)
+{
+	struct regmap *map = drvdata->audio_qaif_map;
+	const struct qaif_variant *v = drvdata->variant;
+	int ret = 0;
+	int qxm_sel = v->qxm_type;
+
+	if (qxm_sel != QXM0) {
+		dev_err(regmap_get_device(map),
+			"%s: only QXM0 is supported, qxm_type=%d\n",
+			__func__, qxm_sel);
+		return -EINVAL;
+	}
+
+	ret |= regmap_write(map, QAIF_RDDMA_MAP_QXM, qxm_sel);
+	ret |= regmap_write(map, QAIF_WRDMA_MAP_QXM, qxm_sel);
+	ret |= regmap_write(map, QAIF_CODEC_RDDMA_MAP_QXM, qxm_sel);
+	ret |= regmap_write(map, QAIF_CODEC_WRDMA_MAP_QXM, qxm_sel);
+
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int qaif_config_shram(struct qaif_drv_data *drvdata)
+{
+	const struct qaif_variant *v = drvdata->variant;
+	u32 start_addr, shram_len;
+	int ret = 0, i = 0;
+	struct regmap *map = drvdata->audio_qaif_map;
+
+	if (v->qxm_type != QXM0) {
+		dev_err(regmap_get_device(map),
+			"%s: only QXM0 is supported, qxm_type=%d\n",
+			__func__, v->qxm_type);
+		return -EINVAL;
+	}
+	start_addr = v->rddma_shram_start_addr[QAIF_AIF_DMA];
+	shram_len = v->rddma_shram_len;
+	for (i = 0; i < v->num_rddma; i++) {
+		ret = regmap_write(map, QAIF_RDDMA_QXM0_SHRAM_ST_ADDR(i), start_addr + (shram_len * i));
+		if (ret)
+			return ret;
+		ret = regmap_write(map, QAIF_RDDMA_QXM0_SHRAM_LEN(i), shram_len);
+		if (ret)
+			return ret;
+	}
+	start_addr = v->wrdma_shram_start_addr[QAIF_AIF_DMA];
+	shram_len = v->wrdma_shram_len;
+	for (i = 0; i < v->num_wrdma; i++) {
+		ret = regmap_write(map, QAIF_WRDMA_QXM0_SHRAM_ST_ADDR(i), start_addr + (shram_len * i));
+		if (ret)
+			return ret;
+		ret = regmap_write(map, QAIF_WRDMA_QXM0_SHRAM_LEN(i), shram_len);
+		if (ret)
+			return ret;
+	}
+	start_addr = v->rddma_shram_start_addr[QAIF_CIF_DMA];
+	shram_len = v->rddma_shram_len;
+	for (i = 0; i < v->num_codec_rddma; i++) {
+		ret = regmap_write(map, QAIF_CODEC_RDDMA_QXM0_SHRAM_ST_ADDR(i), start_addr + (shram_len * i));
+		if (ret)
+			return ret;
+		ret = regmap_write(map, QAIF_CODEC_RDDMA_QXM0_SHRAM_LEN(i), shram_len);
+		if (ret)
+			return ret;
+	}
+	start_addr = v->wrdma_shram_start_addr[QAIF_CIF_DMA];
+	shram_len = v->wrdma_shram_len;
+	for (i = 0; i < v->num_codec_wrdma; i++) {
+		ret = regmap_write(map, QAIF_CODEC_WRDMA_QXM0_SHRAM_ST_ADDR(i), start_addr + (shram_len * i));
+		if (ret)
+			return ret;
+		ret = regmap_write(map, QAIF_CODEC_WRDMA_QXM0_SHRAM_LEN(i), shram_len);
+
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int qaif_init(struct snd_soc_component *component)
+{
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	if (drvdata->qaif_init_ref_cnt) {
+		dev_dbg(component->dev, "%s: QAIF init is done already: ref cnt: %d\n",
+				__func__, drvdata->qaif_init_ref_cnt);
+		return 0;
+	}
+
+	ret = qaif_config_shram(drvdata);
+	if (ret) {
+		dev_err(component->dev, "QAIF: Failed to config shram: %d\n", ret);
+		return ret;
+	}
+
+	ret = qaif_map_ee_resource(drvdata);
+	if (ret) {
+		dev_err(component->dev, "QAIF: Failed to map EE resources: %d\n", ret);
+		return ret;
+	}
+
+	ret = qaif_map_dma_path(drvdata);
+	if (ret) {
+		dev_err(component->dev, "QAIF: Failed to map DMA path: %d\n", ret);
+		return ret;
+	}
+	dev_dbg(component->dev, "%s: QAIF init is done ref cnt: %d\n",
+			__func__, drvdata->qaif_init_ref_cnt);
+	return 0;
+}
-- 
2.34.1


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

* [PATCH 11/13] ASoC: qcom: Add QAIF PCM operations
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (9 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 11:02   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 12/13] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
  2026-06-05 10:37 ` [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add PCM operation callbacks for the QAIF platform driver so AIF and CIF
DAIs can manage the DMA stream lifecycle.

The callbacks allocate and release stream DMA channels, set the ALSA
hardware constraints, manage the stream buffer, program the DMA registers
during prepare, enable or disable DMA and interrupts on trigger, report the
current DMA position to ALSA, and provide mmap support for userspace audio
buffers.

This completes the platform-side PCM support needed for QAIF playback and
capture streams.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif-platform.c | 561 +++++++++++++++++++++++++++++++++
 1 file changed, 534 insertions(+)

diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
index 14e3379ca560..03d390705d4d 100644
--- a/sound/soc/qcom/qaif-platform.c
+++ b/sound/soc/qcom/qaif-platform.c
@@ -274,3 +274,570 @@ static int qaif_init(struct snd_soc_component *component)
 			__func__, drvdata->qaif_init_ref_cnt);
 	return 0;
 }
+
+
+static int qaif_platform_pcmops_open(struct snd_soc_component *component,
+				      struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+	struct snd_dma_buffer *buf;
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	int ret, stream_dma_idx, dir = substream->stream;
+	struct qaif_pcm_data *data;
+	struct qaif_dmactl *dmactl;
+	struct qaif_dma_mem_info *dma_mem_info;
+	struct regmap *map;
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	if (v->alloc_stream_dma_idx)
+		stream_dma_idx = v->alloc_stream_dma_idx(drvdata, dir, dai_id);
+	else
+		return -EINVAL;
+
+	if (stream_dma_idx < 0)
+		return stream_dma_idx;
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		return -ENOMEM;
+	}
+
+	data->stream_dma_idx = stream_dma_idx;
+
+	runtime->private_data = data;
+	map = drvdata->audio_qaif_map;
+	dmactl = qaif_get_dmactl_handle(substream, component);
+	if (!dmactl) {
+		kfree(data);
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		return -EINVAL;
+	}
+	buf = &substream->dma_buffer;
+	buf->dev.dev = component->dev;
+	buf->private_data = NULL;
+	buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
+
+	dma_mem_info = qaif_mem_alloc_attach(component,
+					qaif_platform_aif_hardware.buffer_bytes_max);
+	if (!dma_mem_info) {
+		kfree(data);
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		return -ENOMEM;
+	}
+
+	ret = clk_prepare_enable(drvdata->aud_dma_clk);
+	if (ret) {
+		dev_err(soc_runtime->dev, "failed to enable aud_dma_clk: %d\n", ret);
+		qaif_mem_dealloc_detach(component->dev, dma_mem_info);
+		kfree(data);
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		return ret;
+	}
+	ret = clk_prepare_enable(drvdata->aud_dma_mem_clk);
+	if (ret) {
+		dev_err(soc_runtime->dev, "failed to enable aud_dma_mem_clk: %d\n", ret);
+		clk_disable_unprepare(drvdata->aud_dma_clk);
+		qaif_mem_dealloc_detach(component->dev, dma_mem_info);
+		kfree(data);
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		return ret;
+	}
+
+	ret = qaif_init(component);
+	if (ret) {
+		dev_err(soc_runtime->dev, "qaif_init failed: %d\n", ret);
+		clk_disable_unprepare(drvdata->aud_dma_mem_clk);
+		clk_disable_unprepare(drvdata->aud_dma_clk);
+		qaif_mem_dealloc_detach(component->dev, dma_mem_info);
+		kfree(data);
+		return -EINVAL;
+	}
+	drvdata->qaif_init_ref_cnt++;
+
+	switch (dai_id) {
+	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+		drvdata->aif_substream[stream_dma_idx] = substream;
+		drvdata->aif_dma_heap[stream_dma_idx] = dma_mem_info;
+		buf->bytes = qaif_platform_aif_hardware.buffer_bytes_max;
+		buf->addr = drvdata->aif_dma_heap[stream_dma_idx]->dma_addr;
+		buf->area = (unsigned char *)drvdata->aif_dma_heap[stream_dma_idx]->vaddr;
+
+		snd_soc_set_runtime_hwparams(substream, &qaif_platform_aif_hardware);
+		runtime->dma_bytes = qaif_platform_aif_hardware.buffer_bytes_max;
+		break;
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		drvdata->cif_substream[stream_dma_idx] = substream;
+		drvdata->cif_dma_heap[stream_dma_idx] = dma_mem_info;
+		buf->bytes = qaif_platform_cif_hardware.buffer_bytes_max;
+		buf->addr = drvdata->cif_dma_heap[stream_dma_idx]->dma_addr;
+		buf->area = (unsigned char *)drvdata->cif_dma_heap[stream_dma_idx]->vaddr;
+
+		snd_soc_set_runtime_hwparams(substream, &qaif_platform_cif_hardware);
+		runtime->dma_bytes = qaif_platform_cif_hardware.buffer_bytes_max;
+		break;
+	default:
+		break;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(soc_runtime->dev, "setting constraints failed: %d\n", ret);
+		if (is_cif_dma_port(dai_id)) {
+			drvdata->cif_substream[stream_dma_idx] = NULL;
+			drvdata->cif_dma_heap[stream_dma_idx] = NULL;
+		} else {
+			drvdata->aif_substream[stream_dma_idx] = NULL;
+			drvdata->aif_dma_heap[stream_dma_idx] = NULL;
+		}
+		drvdata->qaif_init_ref_cnt--;
+		clk_disable_unprepare(drvdata->aud_dma_mem_clk);
+		clk_disable_unprepare(drvdata->aud_dma_clk);
+		qaif_mem_dealloc_detach(component->dev, dma_mem_info);
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		kfree(data);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qaif_platform_pcmops_close(struct snd_soc_component *component,
+				       struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_pcm_data *data;
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	data = runtime->private_data;
+
+	switch (dai_id) {
+	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+		drvdata->aif_substream[data->stream_dma_idx] = NULL;
+		qaif_mem_dealloc_detach(component->dev,
+					drvdata->aif_dma_heap[data->stream_dma_idx]);
+		drvdata->aif_dma_heap[data->stream_dma_idx] = NULL;
+		break;
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		drvdata->cif_substream[data->stream_dma_idx] = NULL;
+		qaif_mem_dealloc_detach(component->dev,
+					drvdata->cif_dma_heap[data->stream_dma_idx]);
+		drvdata->cif_dma_heap[data->stream_dma_idx] = NULL;
+		break;
+	default:
+		break;
+	}
+
+	if (drvdata->qaif_init_ref_cnt > 0)
+		drvdata->qaif_init_ref_cnt--;
+	else
+		dev_dbg(component->dev, "%s: QAIF init ref cnt: %d, skipping decrement\n",
+					__func__, drvdata->qaif_init_ref_cnt);
+
+	if (v->free_stream_dma_idx)
+		v->free_stream_dma_idx(drvdata, data->stream_dma_idx, dai_id);
+	clk_disable_unprepare(drvdata->aud_dma_clk);
+	clk_disable_unprepare(drvdata->aud_dma_mem_clk);
+	kfree(data);
+	return 0;
+}
+
+static int qaif_platform_pcmops_hw_params(struct snd_soc_component *component,
+					   struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_dmactl *dmactl;
+	unsigned int dai_id = cpu_dai->driver->id;
+	int idx;
+	int ret;
+
+	dmactl = qaif_get_dmactl_handle(substream, component);
+	if (!dmactl)
+		return -EINVAL;
+	idx = v->get_dma_idx(dai_id);
+
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	ret = regmap_fields_write(dmactl->burst4, idx, QAIF_DMACTL_BURSTEN);
+	if (ret) {
+		dev_err(soc_runtime->dev, "error updating burst4 field: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_fields_write(dmactl->shram_wm, idx, QAIF_DMACTL_WM_5);
+	if (ret) {
+		dev_err(soc_runtime->dev, "error updating shram_wm field: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qaif_platform_pcmops_hw_free(struct snd_soc_component *component,
+					 struct snd_pcm_substream *substream)
+{
+	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 qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	unsigned int reg;
+	int ret, idx;
+	unsigned int dai_id = cpu_dai->driver->id;
+	struct regmap *map = drvdata->audio_qaif_map;
+	struct qaif_dmactl *dmactl;
+
+	dmactl = qaif_get_dmactl_handle(substream, component);
+	if (!dmactl)
+		return -EINVAL;
+	idx = v->get_dma_idx(dai_id);
+
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_OFF);
+	if (ret)
+		dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret);
+
+	reg = QAIF_DMACFG_REG(v, idx, substream->stream, dai_id);
+	ret = regmap_write(map, reg, 0);
+	if (ret)
+		dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret);
+
+	return ret;
+}
+
+static int qaif_platform_pcmops_prepare(struct snd_soc_component *component,
+					 struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_dmactl *dmactl;
+	struct regmap *map;
+	int bitwidth = QAIF_DMA_DEFAULT_BIT_WIDTH;
+	unsigned int channels = runtime->channels;
+	unsigned int rate = runtime->rate;
+	int ret, idx, dir = substream->stream;
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	dmactl = qaif_get_dmactl_handle(substream, component);
+	if (!dmactl)
+		return -EINVAL;
+	idx = v->get_dma_idx(dai_id);
+	map = drvdata->audio_qaif_map;
+
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	clk_set_rate(drvdata->aud_dma_clk, rate * bitwidth * channels * QAIF_DMA_CLK_RATE_MULTIPLIER);
+	clk_set_rate(drvdata->aud_dma_mem_clk, rate * bitwidth * channels * QAIF_DMA_CLK_RATE_MULTIPLIER);
+
+	ret = regmap_write(map, QAIF_SID_MAP_REG(dir, dai_id),
+				drvdata->smmu_csid_bits);
+	if (ret) {
+		dev_err(soc_runtime->dev, "error writing to SID MAP reg: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = regmap_write(map, QAIF_DMABASE_REG(v, idx, dir, dai_id),
+				runtime->dma_addr);
+	if (ret) {
+		dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = regmap_write(map, QAIF_DMABUFF_REG(v, idx, dir, dai_id),
+			(snd_pcm_lib_buffer_bytes(substream) >> QAIF_DMA_BYTES_TO_WORDS_SHIFT) - 1);
+	if (ret) {
+		dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = regmap_write(map, QAIF_DMAPER_LEN_REG(v, idx, dir, dai_id),
+			(snd_pcm_lib_period_bytes(substream) >> QAIF_DMA_BYTES_TO_WORDS_SHIFT) - 1);
+	if (ret) {
+		dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qaif_platform_irq_clear(struct qaif_drv_data *drvdata,
+				int dir, enum qaif_irq_type irq_type, int idx)
+{
+	int ret = 0;
+	const struct qaif_variant *v = drvdata->variant;
+	struct regmap *map = drvdata->audio_qaif_map;
+	unsigned int val_irqclr = BIT(idx);
+
+	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret |= regmap_write(map, QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, irq_type), val_irqclr);
+		ret |= regmap_write(map, QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, irq_type), val_irqclr);
+		ret |= regmap_write(map, QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, irq_type), val_irqclr);
+	} else {
+		ret |= regmap_write(map, QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, irq_type), val_irqclr);
+		ret |= regmap_write(map, QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, irq_type), val_irqclr);
+		ret |= regmap_write(map, QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, irq_type), val_irqclr);
+	}
+	return ret;
+}
+
+static int qaif_platform_irq_enable(struct qaif_drv_data *drvdata,
+				int dir, enum qaif_irq_type irq_type, int idx)
+{
+	int ret = 0;
+	const struct qaif_variant *v = drvdata->variant;
+	struct regmap *map = drvdata->audio_qaif_map;
+	unsigned int val_irqen = BIT(idx);
+
+	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret |= regmap_write_bits(map, QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, irq_type), val_irqen, val_irqen);
+		ret |= regmap_write_bits(map, QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, irq_type), val_irqen, val_irqen);
+		ret |= regmap_write_bits(map, QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), val_irqen, val_irqen);
+	} else {
+		ret |= regmap_write_bits(map, QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, irq_type), val_irqen, val_irqen);
+		ret |= regmap_write_bits(map, QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, irq_type), val_irqen, val_irqen);
+		ret |= regmap_write_bits(map, QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), val_irqen, val_irqen);
+	}
+	return ret;
+}
+
+static int qaif_platform_irq_disable(struct qaif_drv_data *drvdata,
+				int dir, enum qaif_irq_type irq_type, int idx)
+{
+	int ret = 0;
+	const struct qaif_variant *v = drvdata->variant;
+	struct regmap *map = drvdata->audio_qaif_map;
+	unsigned int val_irq_disable = BIT(idx);
+
+	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret |= regmap_write_bits(map, QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, irq_type), val_irq_disable, 0);
+		ret |= regmap_write_bits(map, QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, irq_type), val_irq_disable, 0);
+		ret |= regmap_write_bits(map, QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), val_irq_disable, 0);
+	} else {
+		ret |= regmap_write_bits(map, QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, irq_type), val_irq_disable, 0);
+		ret |= regmap_write_bits(map, QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, irq_type), val_irq_disable, 0);
+		ret |= regmap_write_bits(map, QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), val_irq_disable, 0);
+	}
+	return ret;
+}
+
+static int qaif_platform_pcmops_trigger(struct snd_soc_component *component,
+					 struct snd_pcm_substream *substream,
+					 int cmd)
+{
+	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 qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	struct qaif_dmactl *dmactl;
+	struct regmap *map;
+	int ret, idx;
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	dmactl = qaif_get_dmactl_handle(substream, component);
+	if (!dmactl)
+		return -EINVAL;
+	idx = v->get_dma_idx(dai_id);
+	map = drvdata->audio_qaif_map;
+
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = regmap_fields_write(dmactl->dma_dyncclk, idx, QAIF_DMACTL_DYNCLK_ON);
+		if (ret) {
+			dev_err(soc_runtime->dev,
+				"error writing to dma_dyncclk reg field: %d\n", ret);
+			return ret;
+		}
+		ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_ON);
+		if (ret) {
+			dev_err(soc_runtime->dev,
+				"error writing to dma enable reg: %d\n", ret);
+			return ret;
+		}
+		switch (dai_id) {
+		case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+			ret = qaif_platform_irq_clear(drvdata, substream->stream, QAIF_AIF_IRQ, idx);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to clear irq reg: %d\n", ret);
+				return ret;
+			}
+			ret = qaif_platform_irq_enable(drvdata, substream->stream, QAIF_AIF_IRQ, idx);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to enable irq reg: %d\n", ret);
+				return ret;
+			}
+			break;
+		case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+		case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+		case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+			ret = qaif_platform_irq_clear(drvdata, substream->stream, QAIF_CIF_IRQ, idx);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to clear irq reg: %d\n", ret);
+				return ret;
+			}
+			ret = qaif_platform_irq_enable(drvdata, substream->stream, QAIF_CIF_IRQ, idx);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to enable irq reg: %d\n", ret);
+				return ret;
+			}
+			break;
+		default:
+			dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+			return -EINVAL;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = regmap_fields_write(dmactl->dma_dyncclk, idx, QAIF_DMACTL_DYNCLK_OFF);
+		if (ret) {
+			dev_err(soc_runtime->dev,
+				"error writing to dma_dyncclk reg field: %d\n", ret);
+			return ret;
+		}
+		ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_OFF);
+		if (ret) {
+			dev_err(soc_runtime->dev,
+				"error writing to dma enable reg: %d\n", ret);
+			return ret;
+		}
+		switch (dai_id) {
+		case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+			ret = qaif_platform_irq_disable(drvdata, substream->stream, QAIF_AIF_IRQ, idx);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to enable irq reg: %d\n", ret);
+				return ret;
+			}
+			break;
+		case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+		case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+		case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+			ret = qaif_platform_irq_disable(drvdata, substream->stream, QAIF_CIF_IRQ, idx);
+			if (ret) {
+				dev_err(soc_runtime->dev,
+					"error writing to enable irq reg: %d\n", ret);
+				return ret;
+			}
+			break;
+		default:
+			dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t qaif_platform_pcmops_pointer(
+		struct snd_soc_component *component,
+		struct snd_pcm_substream *substream)
+{
+	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 qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	const struct qaif_variant *v = drvdata->variant;
+	unsigned int base_addr, curr_addr;
+	int ret, idx, dir = substream->stream;
+	struct regmap *map;
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	map = drvdata->audio_qaif_map;
+	idx = v->get_dma_idx(dai_id);
+
+	if (idx < 0) {
+		dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	ret = regmap_read(map,
+			QAIF_DMABASE_REG(v, idx, dir, dai_id), &base_addr);
+	if (ret) {
+		dev_err(soc_runtime->dev,
+			"error reading from rdmabase reg: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(map,
+			QAIF_DMACURR_REG(v, idx, dir, dai_id), &curr_addr);
+	if (ret) {
+		dev_err(soc_runtime->dev,
+			"error reading from rdmacurr reg: %d\n", ret);
+		return ret;
+	}
+
+	return bytes_to_frames(substream->runtime, curr_addr - base_addr);
+}
+
+static int qaif_platform_cdc_dma_mmap(struct snd_pcm_substream *substream,
+				       struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	return dma_mmap_coherent(substream->pcm->card->dev, vma,
+				 runtime->dma_area, runtime->dma_addr,
+				 runtime->dma_bytes);
+}
+
+static int qaif_platform_pcmops_mmap(struct snd_soc_component *component,
+				      struct snd_pcm_substream *substream,
+				      struct vm_area_struct *vma)
+{
+	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);
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	if (is_cif_dma_port(dai_id))
+		return qaif_platform_cdc_dma_mmap(substream, vma);
+
+	return snd_pcm_lib_default_mmap(substream, vma);
+}
-- 
2.34.1


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

* [PATCH 12/13] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (10 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 11/13] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 11:13   ` sashiko-bot
  2026-06-05 10:37 ` [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
  12 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Complete the QAIF PCM platform support by registering the component,
requesting the QAIF interrupt, and handling DMA interrupt events.

The QAIF hardware reports DMA events through a summary interrupt register
and per-DMA status registers. Add the top-level IRQ handler and AIF/CIF DMA
handlers so period interrupts notify ALSA with snd_pcm_period_elapsed(),
xrun conditions are reported, and bus errors stop the affected stream.

Add suspend and resume callbacks to put the QAIF regmap into cache-only
mode while the clocks are disabled, then re-enable the clocks and sync the
cached register state on resume.

Read the SMMU stream ID from the iommus property during platform
registration so the existing resource setup code can program the SID
mapping correctly. Also add the component copy callback for transferring
PCM data through the runtime DMA buffer.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/qaif-platform.c | 309 +++++++++++++++++++++++++++++++++
 1 file changed, 309 insertions(+)

diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
index 03d390705d4d..38130ee89930 100644
--- a/sound/soc/qcom/qaif-platform.c
+++ b/sound/soc/qcom/qaif-platform.c
@@ -808,3 +808,323 @@ static int qaif_platform_pcmops_mmap(struct snd_soc_component *component,
 
 	return snd_pcm_lib_default_mmap(substream, vma);
 }
+
+static irqreturn_t qaif_process_dma_irq(struct qaif_drv_data *drvdata,
+								u32 stat_reg_addr,
+								u32 clr_reg_addr,
+								enum qaif_irq_type irq_type,
+								enum dma_type dma_type,
+								enum qaif_irq irq,
+								struct snd_pcm_substream **substream)
+{
+	const struct qaif_variant *v = drvdata->variant;
+	struct snd_pcm_substream *stream = NULL;
+	unsigned int reg = 0;
+	int dma_idx, stream_dma_idx, rv, num_dma = 0;
+	int stream_offset;
+	irqreturn_t ret = IRQ_NONE;
+	u32 mask = 0;
+
+	if (dma_type == DMA_TYPE_WRDMA)
+		stream_offset = (irq_type == QAIF_AIF_IRQ) ? v->wrdma_start
+							    : v->codec_wrdma_start;
+	else
+		stream_offset = 0;
+
+	if (dma_type == DMA_TYPE_WRDMA)
+		num_dma = (irq_type == QAIF_AIF_IRQ) ? v->num_wrdma : v->num_codec_wrdma;
+	else
+		num_dma = (irq_type == QAIF_AIF_IRQ) ? v->num_rddma : v->num_codec_rddma;
+	mask = GENMASK(num_dma-1, 0);
+	rv = regmap_read(drvdata->audio_qaif_map, stat_reg_addr, &reg);
+	if (rv) {
+		dev_err(regmap_get_device(drvdata->audio_qaif_map),
+			"error reading stat reg 0x%x: %d\n", stat_reg_addr, rv);
+		return IRQ_NONE;
+	}
+
+	regmap_write(drvdata->audio_qaif_map, clr_reg_addr, reg & mask);
+
+	for (dma_idx = 0; dma_idx < num_dma; dma_idx++) {
+		stream_dma_idx = dma_idx + stream_offset;
+		if ((reg & BIT(dma_idx)) && substream[stream_dma_idx]) {
+			stream = substream[stream_dma_idx];
+			switch (irq) {
+			case QAIF_IRQ_PERIOD:
+				snd_pcm_period_elapsed(stream);
+				ret = IRQ_HANDLED;
+				break;
+
+			case QAIF_IRQ_OVERFLOW:
+			case QAIF_IRQ_UNDERFLOW:
+				dev_warn_ratelimited(regmap_get_device(drvdata->audio_qaif_map),
+						     "QAIF DMA xRun\n");
+				ret = IRQ_HANDLED;
+				break;
+
+			case QAIF_IRQ_ERROR:
+				snd_pcm_stop(stream, SNDRV_PCM_STATE_DISCONNECTED);
+				dev_err(regmap_get_device(drvdata->audio_qaif_map),
+					"QAIF bus error\n");
+				ret = IRQ_HANDLED;
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+static irqreturn_t qaif_aif_irq_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status)
+{
+	const struct qaif_variant *v = drvdata->variant;
+	irqreturn_t ret = IRQ_NONE;
+	struct snd_pcm_substream **substream = drvdata->aif_substream;
+
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_PERIOD_RDDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+			QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+			QAIF_AIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_PERIOD, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_PERIOD_WRDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+			QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+			QAIF_AIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_PERIOD, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_OVERFLOW_WRDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+			QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+			QAIF_AIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_OVERFLOW, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_UNDERFLOW_RDDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+			QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+			QAIF_AIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_UNDERFLOW, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_RDDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+			QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+			QAIF_AIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_ERROR, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_WRDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+			QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+			QAIF_AIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_ERROR, substream);
+	}
+	return ret;
+}
+
+static irqreturn_t qaif_cif_irq_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status)
+{
+	const struct qaif_variant *v = drvdata->variant;
+	irqreturn_t ret = IRQ_NONE;
+	struct snd_pcm_substream **substream = drvdata->cif_substream;
+
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_PERIOD_RDDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+			QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+			QAIF_CIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_PERIOD, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_PERIOD_WRDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+			QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+			QAIF_CIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_PERIOD, substream);
+	}
+
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_OVERFLOW_WRDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+			QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+			QAIF_CIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_OVERFLOW, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_UNDERFLOW_RDDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+			QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+			QAIF_CIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_UNDERFLOW, substream);
+	}
+
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_RDDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+			QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+			QAIF_CIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_ERROR, substream);
+	}
+	if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_WRDMA) {
+		ret |= qaif_process_dma_irq(drvdata,
+			QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+			QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+			QAIF_CIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_ERROR, substream);
+	}
+
+	return ret;
+}
+
+
+#define QAIF_ALL_CLIENTS_MASK (QAIF_BITMASK_AIF_RDDMA_WRDMA | QAIF_BITMASK_CIF_RDDMA_WRDMA)
+
+static const struct qaif_irq_map qaif_irq_clients[] = {
+	{ QAIF_CLIENT_ID_AIF_DMA,	QAIF_BITMASK_AIF_RDDMA_WRDMA, qaif_aif_irq_handler},
+	{ QAIF_CLIENT_ID_CIF_DMA,	QAIF_BITMASK_CIF_RDDMA_WRDMA, qaif_cif_irq_handler},
+};
+
+static irqreturn_t asoc_platform_qaif_irq(int irq, void *data)
+{
+	struct qaif_drv_data *drvdata = data;
+	const struct qaif_variant *v = drvdata->variant;
+	u32 summary_irq_status;
+	int rv, client;
+	irqreturn_t ret = IRQ_NONE;
+
+	rv = regmap_read(drvdata->audio_qaif_map,
+			QAIF_SUMMARY_IRQSTAT_REG(v), &summary_irq_status);
+	if (rv) {
+		dev_err(regmap_get_device(drvdata->audio_qaif_map),
+			"error reading from irqstat reg: %d\n", rv);
+		return IRQ_NONE;
+	}
+	if (!(summary_irq_status & QAIF_ALL_CLIENTS_MASK))
+		return IRQ_NONE;
+	for (client = 0; client < ARRAY_SIZE(qaif_irq_clients); client++) {
+		if (summary_irq_status & qaif_irq_clients[client].mask)
+			ret |= qaif_irq_clients[client].client_irq_handler(drvdata, summary_irq_status);
+	}
+	return ret;
+}
+
+static int qaif_platform_pcmops_suspend(struct snd_soc_component *component)
+{
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	struct regmap *map;
+
+	map = drvdata->audio_qaif_map;
+
+	regcache_cache_only(map, true);
+	regcache_mark_dirty(map);
+	if (drvdata->qaif_init_ref_cnt > 0) {
+		clk_disable(drvdata->aud_dma_clk);
+		clk_disable(drvdata->aud_dma_mem_clk);
+	}
+	return 0;
+}
+
+static int qaif_platform_pcmops_resume(struct snd_soc_component *component)
+{
+	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+	struct regmap *map = drvdata->audio_qaif_map;
+	int ret;
+
+	if (drvdata->qaif_init_ref_cnt > 0) {
+		ret = clk_enable(drvdata->aud_dma_clk);
+		if (ret)
+			return ret;
+		ret = clk_enable(drvdata->aud_dma_mem_clk);
+		if (ret) {
+			clk_disable(drvdata->aud_dma_clk);
+			return ret;
+		}
+	}
+	regcache_cache_only(map, false);
+	return regcache_sync(map);
+}
+
+static int qaif_platform_copy(struct snd_soc_component *component,
+			       struct snd_pcm_substream *substream, int channel,
+			       unsigned long pos, struct iov_iter *buf,
+			       unsigned long bytes)
+{
+	struct snd_pcm_runtime *rt = substream->runtime;
+	size_t copied;
+	void *dma_buf;
+
+	dma_buf = (void *)(rt->dma_area + pos +
+			   channel * (rt->dma_bytes / rt->channels));
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		copied = copy_from_iter(dma_buf, bytes, buf);
+		if (copied != bytes)
+			return -EFAULT;
+	} else {
+		copied = copy_to_iter(dma_buf, bytes, buf);
+		if (copied != bytes)
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver qaif_component_driver = {
+	.name		= DRV_NAME,
+	.open		= qaif_platform_pcmops_open,
+	.close		= qaif_platform_pcmops_close,
+	.hw_params	= qaif_platform_pcmops_hw_params,
+	.hw_free	= qaif_platform_pcmops_hw_free,
+	.prepare	= qaif_platform_pcmops_prepare,
+	.trigger	= qaif_platform_pcmops_trigger,
+	.pointer	= qaif_platform_pcmops_pointer,
+	.mmap		= qaif_platform_pcmops_mmap,
+	.suspend	= qaif_platform_pcmops_suspend,
+	.resume		= qaif_platform_pcmops_resume,
+	.copy		= qaif_platform_copy,
+};
+static int qaif_parse_smmu_sid(struct platform_device *pdev,
+				struct qaif_drv_data *drvdata)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+	u32 sid;
+
+	ret = of_property_read_u32_index(np, "iommus", 1, &sid);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to read SMMU SID from iommus property: %d\n", ret);
+		return ret;
+	}
+
+	drvdata->smmu_csid_bits = sid & 0x1;
+	dev_dbg(&pdev->dev, "sid=0x%x csid=0x%x\n", sid, drvdata->smmu_csid_bits);
+	return 0;
+}
+
+int asoc_qcom_qaif_platform_register(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+	if (!drvdata || !drvdata->variant) {
+		dev_err(&pdev->dev, "Invalid drvdata or variant\n");
+		return -EINVAL;
+	}
+
+	ret = qaif_parse_smmu_sid(pdev, drvdata);
+	if (ret)
+		return ret;
+
+	drvdata->audio_qaif_irq = platform_get_irq(pdev, 0);
+	if (drvdata->audio_qaif_irq < 0)
+		return drvdata->audio_qaif_irq;
+
+	ret = devm_request_irq(&pdev->dev, drvdata->audio_qaif_irq,
+			asoc_platform_qaif_irq, IRQF_TRIGGER_HIGH,
+			"qaif-irq-audio-core", drvdata);
+	if (ret) {
+		dev_err(&pdev->dev, "irq request failed: %d\n", ret);
+		return ret;
+	}
+	drvdata->qaif_init_ref_cnt = 0;
+	dev_dbg(&pdev->dev, "%s: Register QAIF Platform\n", __func__);
+	return devm_snd_soc_register_component(&pdev->dev,
+			&qaif_component_driver, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_platform_register);
+
+MODULE_DESCRIPTION("Qualcomm Audio Interface (QAIF) PCM platform driver");
+MODULE_AUTHOR("Harendra Gautam <harendra.gautam@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
-- 
2.34.1


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

* [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support
  2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (11 preceding siblings ...)
  2026-06-05 10:37 ` [PATCH 12/13] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
@ 2026-06-05 10:37 ` Harendra Gautam
  2026-06-05 10:58   ` Krzysztof Kozlowski
  2026-06-05 11:02   ` sashiko-bot
  12 siblings, 2 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-06-05 10:37 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

Add the Shikra variant data for the Qualcomm Audio Interface driver and
hook it up to Kconfig and the qcom ASoC Makefile.

Shikra uses QAIF to move PCM data between memory, MI2S interfaces and the
Bolero codec CDC DMA paths. Without the SoC-specific register layout, DMA-
to-DAI mappings, clock list and DAI descriptors, Shikra-based platforms
cannot enable playback or capture through this block.

Provide the Shikra QAIF register field definitions, DMA mappings, DAI
descriptors, clock configuration and platform match data, and build them as
part of the snd-soc-qcom-qaif module.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/Kconfig       |  11 +
 sound/soc/qcom/Makefile      |   2 +
 sound/soc/qcom/qaif-shikra.c | 585 +++++++++++++++++++++++++++++++++++
 3 files changed, 598 insertions(+)
 create mode 100644 sound/soc/qcom/qaif-shikra.c

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index e6e24f3b9922..2eca25478fd4 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -71,6 +71,17 @@ config SND_SOC_QCOM_COMMON
 config SND_SOC_QCOM_SDW
 	tristate
 
+config SND_SOC_QCOM_QAIF
+	tristate "Qualcomm QAIF audio interface support"
+	depends on COMMON_CLK
+	select REGMAP_MMIO
+	help
+	  Say Y or M to enable the Qualcomm Audio Interface (QAIF) driver
+	  used on the Shikra audio platform. QAIF is the DMA controller
+	  that moves PCM data between the codec and memory over AIF (MI2S)
+	  and CIF (CDC DMA) paths. Required for audio playback and capture
+	  on Shikra-based platforms.
+
 config SND_SOC_QDSP6_COMMON
 	tristate
 
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 985ce2ae286b..857bb2a032a2 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -31,6 +31,7 @@ snd-soc-qcom-common-y := common.o
 snd-soc-qcom-sdw-y := sdw.o
 snd-soc-x1e80100-y := x1e80100.o
 snd-soc-qcom-offload-utils-objs := usb_offload_utils.o
+snd-soc-qcom-qaif-y := qaif-cpu.o qaif-platform.o qaif-shikra.o
 
 obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
 obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
@@ -44,6 +45,7 @@ obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
 obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o
 obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o
 obj-$(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) += snd-soc-qcom-offload-utils.o
+obj-$(CONFIG_SND_SOC_QCOM_QAIF) += snd-soc-qcom-qaif.o
 
 #DSP lib
 obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/
diff --git a/sound/soc/qcom/qaif-shikra.c b/sound/soc/qcom/qaif-shikra.c
new file mode 100644
index 000000000000..e83564503087
--- /dev/null
+++ b/sound/soc/qcom/qaif-shikra.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * qaif-shikra.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF
+ */
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/pm.h>
+#include "qaif.h"
+
+struct qaif_dmaidx_dai_map shikra_aif_dma_dai_map[] = {
+		{ QAIF_MI2S_TDM_AIF0 },
+		{ QAIF_MI2S_TDM_AIF1 },
+		{ QAIF_MI2S_TDM_AIF2 },
+		{ QAIF_MI2S_TDM_AIF3 }
+};
+
+struct qaif_dmaidx_dai_map shikra_cif_rx_dma_dai_map[] = {
+		{ QAIF_CDC_DMA_RX0 },
+		{ QAIF_CDC_DMA_RX1 },
+		{ QAIF_CDC_DMA_RX2 },
+		{ QAIF_CDC_DMA_RX3 }
+};
+
+struct qaif_dmaidx_dai_map shikra_cif_tx_dma_dai_map[] = {
+		{ QAIF_CDC_DMA_TX0 },
+		{ QAIF_CDC_DMA_TX1 },
+		{ QAIF_CDC_DMA_TX2 },
+		{ QAIF_CDC_DMA_TX3 }
+};
+
+struct qaif_dmaidx_dai_map shikra_cif_va_dma_dai_map[] = {
+		{ QAIF_CDC_DMA_VA_TX0 },
+		{ QAIF_CDC_DMA_VA_TX1 },
+		{ QAIF_CDC_DMA_VA_TX2 },
+		{ QAIF_CDC_DMA_VA_TX3 }
+};
+
+static struct snd_soc_dai_driver shikra_qaif_cpu_dai_driver[] = {
+	{
+		.id = QAIF_MI2S_TDM_AIF0,
+		.name = "Audio Interface Zero",
+		.playback = {
+			.stream_name = "AIF Zero Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.capture = {
+			.stream_name = "AIF Zero Capture",
+			.formats = SNDRV_PCM_FMTBIT_S16 |
+				SNDRV_PCM_FMTBIT_S32,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops = &asoc_qcom_qaif_aif_cpu_dai_ops,
+	}, {
+		.id = QAIF_MI2S_TDM_AIF1,
+		.name = "Audio Interface One",
+		.playback = {
+			.stream_name = "AIF One Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.capture = {
+			.stream_name = "AIF One Capture",
+			.formats = SNDRV_PCM_FMTBIT_S16 |
+				SNDRV_PCM_FMTBIT_S32,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops = &asoc_qcom_qaif_aif_cpu_dai_ops,
+	}, {
+		.id = QAIF_MI2S_TDM_AIF2,
+		.name = "Audio Interface Two",
+		.playback = {
+			.stream_name = "AIF Two Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.capture = {
+			.stream_name = "AIF Two Capture",
+			.formats = SNDRV_PCM_FMTBIT_S16 |
+				SNDRV_PCM_FMTBIT_S32,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops = &asoc_qcom_qaif_aif_cpu_dai_ops,
+	}, {
+		.id = QAIF_MI2S_TDM_AIF3,
+		.name = "Audio Interface Three",
+		.playback = {
+			.stream_name = "AIF Three Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.capture = {
+			.stream_name = "AIF Three Capture",
+			.formats = SNDRV_PCM_FMTBIT_S16 |
+				SNDRV_PCM_FMTBIT_S32,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 2,
+			.channels_max	= 2,
+		},
+		.ops = &asoc_qcom_qaif_aif_cpu_dai_ops,
+	}, {
+		.id = QAIF_CDC_DMA_RX0,
+		.name = "CDC DMA RX0",
+		.playback = {
+			.stream_name = "WCD Playback0",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 1,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_qaif_cif_dai_ops,
+	}, {
+		.id = QAIF_CDC_DMA_RX1,
+		.name = "CDC DMA RX1",
+		.playback = {
+			.stream_name = "WCD Playback1",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 1,
+			.channels_max	= 2,
+		},
+		.ops	= &asoc_qcom_qaif_cif_dai_ops,
+	}, {
+		.id = QAIF_CDC_DMA_VA_TX0,
+		.name = "CDC DMA VA0",
+		.capture = {
+			.stream_name = "DMIC Capture0",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 1,
+			.channels_max	= 4,
+		},
+		.ops	= &asoc_qcom_qaif_cif_dai_ops,
+	}, {
+		.id = QAIF_CDC_DMA_VA_TX1,
+		.name = "CDC DMA VA1",
+		.capture = {
+			.stream_name = "DMIC Capture1",
+			.formats = SNDRV_PCM_FMTBIT_S16,
+			.rates = SNDRV_PCM_RATE_48000,
+			.rate_min	= 48000,
+			.rate_max	= 48000,
+			.channels_min	= 1,
+			.channels_max	= 4,
+		},
+		.ops	= &asoc_qcom_qaif_cif_dai_ops,
+	},
+};
+
+static int shikra_qaif_get_dma_idx(unsigned int dai_id)
+{
+	int i;
+
+	switch (dai_id) {
+	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+		for (i = 0; i < ARRAY_SIZE(shikra_aif_dma_dai_map); i++) {
+			if (shikra_aif_dma_dai_map[i].dai_id == dai_id)
+				return i;
+		}
+		break;
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+		for (i = 0; i < ARRAY_SIZE(shikra_cif_rx_dma_dai_map); i++) {
+			if (shikra_cif_rx_dma_dai_map[i].dai_id == dai_id)
+				return i;
+		}
+		break;
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+		for (i = 0; i < ARRAY_SIZE(shikra_cif_tx_dma_dai_map); i++) {
+			if (shikra_cif_tx_dma_dai_map[i].dai_id == dai_id)
+				return i;
+		}
+		break;
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		for (i = 0; i < ARRAY_SIZE(shikra_cif_va_dma_dai_map); i++) {
+			if (shikra_cif_va_dma_dai_map[i].dai_id == dai_id)
+				return i;
+		}
+		break;
+	default:
+		pr_debug("DAI ID not Supported\n");
+		break;
+	}
+
+	pr_debug("DAI ID %u not found in map\n", dai_id);
+	return -EINVAL;
+}
+
+static int shikra_qaif_alloc_stream_dma_idx(struct qaif_drv_data *drvdata,
+					  int direction, unsigned int dai_id)
+{
+	const struct qaif_variant *v = drvdata->variant;
+	int dma_idx;
+	int index = 0;
+
+	if (!v)
+		return -EINVAL;
+
+	switch (dai_id) {
+	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+		dma_idx = shikra_qaif_get_dma_idx(dai_id);
+		if (dma_idx < 0)
+			return dma_idx;
+
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			index = dma_idx;
+			if (index >= v->num_rddma)
+				return -EBUSY;
+		} else {
+			index = v->wrdma_start + dma_idx;
+			if (index >= v->wrdma_start + v->num_wrdma)
+				return -EBUSY;
+		}
+		if (test_bit(index, &drvdata->aif_dma_idx_bit_map))
+			return -EBUSY;
+
+		set_bit(index, &drvdata->aif_dma_idx_bit_map);
+		break;
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		dma_idx = shikra_qaif_get_dma_idx(dai_id);
+		if (dma_idx < 0)
+			return dma_idx;
+
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			index = dma_idx;
+			if (index >= v->num_codec_rddma)
+				return -EBUSY;
+		} else {
+			index = v->codec_wrdma_start + dma_idx;
+			if (index >= v->codec_wrdma_start + v->num_codec_wrdma)
+				return -EBUSY;
+		}
+		if (test_bit(index, &drvdata->cif_dma_idx_bit_map))
+			return -EBUSY;
+
+		set_bit(index, &drvdata->cif_dma_idx_bit_map);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return index;
+}
+
+static int shikra_qaif_free_stream_dma_idx(struct qaif_drv_data *drvdata,
+				 int index, unsigned int dai_id)
+{
+	switch (dai_id) {
+	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
+		clear_bit(index, &drvdata->aif_dma_idx_bit_map);
+		break;
+	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
+	case QAIF_CDC_DMA_TX0 ... QAIF_CDC_DMA_TX9:
+	case QAIF_CDC_DMA_VA_TX0 ... QAIF_CDC_DMA_VA_TX9:
+		clear_bit(index, &drvdata->cif_dma_idx_bit_map);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int shikra_qaif_init(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+	const struct qaif_variant *v = drvdata->variant;
+	struct device *dev = &pdev->dev;
+	int ret, i;
+
+	if (!v) {
+		dev_err(dev, "No variant data\n");
+		return -EINVAL;
+	}
+	if (v->num_clks == 0 || v->num_clks > QAIF_MAX_VARIANT_CLKS) {
+		dev_err(dev, "Invalid clock count: %d\n", v->num_clks);
+		return -EINVAL;
+	}
+	drvdata->clks = devm_kcalloc(dev, v->num_clks,
+				     sizeof(*drvdata->clks), GFP_KERNEL);
+	if (!drvdata->clks)
+		return -ENOMEM;
+
+	drvdata->num_clks = v->num_clks;
+
+	for (i = 0; i < drvdata->num_clks; i++)
+		drvdata->clks[i].id = v->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, "shikra clk_enable failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int shikra_qaif_exit(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+
+	if (!drvdata || !drvdata->clks)
+		return -EINVAL;
+
+	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+	return 0;
+}
+
+static int __maybe_unused shikra_qaif_dev_resume(struct device *dev)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+
+	if (!drvdata || !drvdata->clks) {
+		dev_err(dev, "Invalid drvdata in resume\n");
+		return -EINVAL;
+	}
+	return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+}
+
+static int __maybe_unused shikra_qaif_dev_suspend(struct device *dev)
+{
+	struct qaif_drv_data *drvdata = dev_get_drvdata(dev);
+
+	if (!drvdata || !drvdata->clks) {
+		dev_err(dev, "Invalid drvdata in suspend\n");
+		return -EINVAL;
+	}
+	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
+	return 0;
+}
+
+static const struct dev_pm_ops shikra_qaif_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(shikra_qaif_dev_suspend,
+					shikra_qaif_dev_resume)
+};
+
+static const struct qaif_variant shikra_qaif_data = {
+	.ee = 0,
+	.qaif_type = QAIF,
+
+	.num_rddma = 4,
+	.num_wrdma = 4,
+	.wrdma_start = 4,
+
+	.num_codec_rddma = 4,
+	.num_codec_wrdma = 4,
+	.codec_wrdma_start = 4,
+	.num_intf = 4,
+
+	.rddma_reg_base = 0x8000,
+	.rddma_stride = 0x1000,
+	.codec_rddma_reg_base = 0xC000,
+	.codec_rddma_stride = 0x1000,
+
+	.wrdma_reg_base = 0x11000,
+	.wrdma_stride = 0x1000,
+	.codec_wrdma_reg_base = 0x15000,
+	.codec_wrdma_stride = 0x1000,
+
+	.rddma_irq_reg_base = 0x19000,
+	.rddma_irq_stride = 0x1000,
+	.codec_rddma_irq_reg_base = 0x191A0,
+	.codec_rddma_irq_stride = 0x1000,
+
+	.wrdma_irq_reg_base = 0x19078,
+	.wrdma_irq_stride = 0x1000,
+	.codec_wrdma_irq_reg_base = 0x19290,
+	.codec_wrdma_irq_stride = 0x1000,
+
+	.qxm_type = QXM0,
+	.rd_len = 512,
+	.rddma_shram_len = 64,
+	.rddma_shram_start_addr = {0, 256},
+	.wr_len = 512,
+	.wrdma_shram_len = 64,
+	.wrdma_shram_start_addr = {0, 256},
+
+	/* AUDIO_CORE_QAIF_RDDMAa_CTL (0x8000 + 0x1000*a) */
+	.rddma_enable							= REG_FIELD_ID(0x8000, 0, 0, 4, 0x1000),
+	.rddma_reset							= REG_FIELD_ID(0x8000, 4, 4, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_RDDMAa_CFG (0x8004 + 0x1000*a) */
+	.rddma_shram_wm							= REG_FIELD_ID(0x8004, 0, 11, 4, 0x1000),
+	.rddma_burst1							= REG_FIELD_ID(0x8004, 16, 16, 4, 0x1000),
+	.rddma_burst2							= REG_FIELD_ID(0x8004, 17, 17, 4, 0x1000),
+	.rddma_burst4							= REG_FIELD_ID(0x8004, 18, 18, 4, 0x1000),
+	.rddma_burst8							= REG_FIELD_ID(0x8004, 19, 19, 4, 0x1000),
+	.rddma_burst16							= REG_FIELD_ID(0x8004, 20, 20, 4, 0x1000),
+	.rddma_dma_dyncclk						= REG_FIELD_ID(0x8004, 24, 24, 4, 0x1000),
+	.rddma_num_ot							= REG_FIELD_ID(0x8004, 28, 29, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_WRDMAa_CTL (0x11000 + 0x1000*a) */
+	.wrdma_enable							= REG_FIELD_ID(0x11000, 0, 0, 4, 0x1000),
+	.wrdma_reset							= REG_FIELD_ID(0x11000, 4, 4, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_WRDMAa_CFG (0x11004 + 0x1000*a) */
+	.wrdma_shram_wm							= REG_FIELD_ID(0x11004, 0, 11, 4, 0x1000),
+	.wrdma_burst1							= REG_FIELD_ID(0x11004, 16, 16, 4, 0x1000),
+	.wrdma_burst2							= REG_FIELD_ID(0x11004, 17, 17, 4, 0x1000),
+	.wrdma_burst4							= REG_FIELD_ID(0x11004, 18, 18, 4, 0x1000),
+	.wrdma_burst8							= REG_FIELD_ID(0x11004, 19, 19, 4, 0x1000),
+	.wrdma_burst16							= REG_FIELD_ID(0x11004, 20, 20, 4, 0x1000),
+	.wrdma_dma_dyncclk						= REG_FIELD_ID(0x11004, 24, 24, 4, 0x1000),
+	.wrdma_num_ot							= REG_FIELD_ID(0x11004, 28, 29, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_CODEC_RDDMAa_CTL (0xC000 + 0x1000*a) */
+	.cif_rddma_enable						= REG_FIELD_ID(0xC000, 0, 0, 4, 0x1000),
+	.cif_rddma_reset						= REG_FIELD_ID(0xC000, 4, 4, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_CODEC_RDDMAa_CFG (0xC004 + 0x1000*a) */
+	.cif_rddma_shram_wm						= REG_FIELD_ID(0xC004, 0, 11, 4, 0x1000),
+	.cif_rddma_burst1						= REG_FIELD_ID(0xC004, 16, 16, 4, 0x1000),
+	.cif_rddma_burst2						= REG_FIELD_ID(0xC004, 17, 17, 4, 0x1000),
+	.cif_rddma_burst4						= REG_FIELD_ID(0xC004, 18, 18, 4, 0x1000),
+	.cif_rddma_burst8						= REG_FIELD_ID(0xC004, 19, 19, 4, 0x1000),
+	.cif_rddma_burst16						= REG_FIELD_ID(0xC004, 20, 20, 4, 0x1000),
+	.cif_rddma_dma_dyncclk					= REG_FIELD_ID(0xC004, 24, 24, 4, 0x1000),
+	.cif_rddma_num_ot						= REG_FIELD_ID(0xC004, 28, 29, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_CODEC_RDDMAa_INTF_CFG (0xC05C + 0x1000*a) */
+	.cif_rddma_en_16bit_unpack				= REG_FIELD_ID(0xC05C, 0, 0, 4, 0x1000),
+	.cif_rddma_intf_dyncclk					= REG_FIELD_ID(0xC05C, 2, 2, 4, 0x1000),
+	.cif_rddma_fs_out_gate					= REG_FIELD_ID(0xC05C, 3, 3, 4, 0x1000),
+	.cif_rddma_fs_sel						= REG_FIELD_ID(0xC05C, 4, 7, 4, 0x1000),
+	.cif_rddma_fs_delay						= REG_FIELD_ID(0xC05C, 8, 11, 4, 0x1000),
+	.cif_rddma_active_ch_en					= REG_FIELD_ID(0xC05C, 12, 27, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_CODEC_WRDMAa_CTL (0x15000 + 0x1000*a) */
+	.cif_wrdma_enable						= REG_FIELD_ID(0x15000, 0, 0, 4, 0x1000),
+	.cif_wrdma_reset						= REG_FIELD_ID(0x15000, 4, 4, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_CODEC_WRDMAa_CFG (0x15004 + 0x1000*a) */
+	.cif_wrdma_shram_wm						= REG_FIELD_ID(0x15004, 0, 11, 4, 0x1000),
+	.cif_wrdma_burst1						= REG_FIELD_ID(0x15004, 16, 16, 4, 0x1000),
+	.cif_wrdma_burst2						= REG_FIELD_ID(0x15004, 17, 17, 4, 0x1000),
+	.cif_wrdma_burst4						= REG_FIELD_ID(0x15004, 18, 18, 4, 0x1000),
+	.cif_wrdma_burst8						= REG_FIELD_ID(0x15004, 19, 19, 4, 0x1000),
+	.cif_wrdma_burst16						= REG_FIELD_ID(0x15004, 20, 20, 4, 0x1000),
+	.cif_wrdma_dma_dyncclk					= REG_FIELD_ID(0x15004, 24, 24, 4, 0x1000),
+	.cif_wrdma_num_ot						= REG_FIELD_ID(0x15004, 28, 29, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_CODEC_WRDMAa_INTF_CFG (0x15058 + 0x1000*a) */
+	.cif_wrdma_en_16bit_unpack				= REG_FIELD_ID(0x15058, 0, 0, 4, 0x1000),
+	.cif_wrdma_intf_dyncclk					= REG_FIELD_ID(0x15058, 2, 2, 4, 0x1000),
+	.cif_wrdma_fs_out_gate					= REG_FIELD_ID(0x15058, 3, 3, 4, 0x1000),
+	.cif_wrdma_fs_sel						= REG_FIELD_ID(0x15058, 4, 7, 4, 0x1000),
+	.cif_wrdma_fs_delay						= REG_FIELD_ID(0x15058, 8, 11, 4, 0x1000),
+	.cif_wrdma_active_ch_en					= REG_FIELD_ID(0x15058, 12, 27, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_CTL (0x4000 + 0x1000*a) */
+	.aif_enable								= REG_FIELD_ID(0x4000, 0, 0, 4, 0x1000),
+	.aif_enable_tx							= REG_FIELD_ID(0x4000, 4, 4, 4, 0x1000),
+	.aif_enable_rx							= REG_FIELD_ID(0x4000, 8, 8, 4, 0x1000),
+	.aif_reset								= REG_FIELD_ID(0x4000, 12, 12, 4, 0x1000),
+	.aif_reset_tx							= REG_FIELD_ID(0x4000, 16, 16, 4, 0x1000),
+	.aif_reset_rx							= REG_FIELD_ID(0x4000, 20, 20, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_SYNC_CFG (0x4004 + 0x1000*a) */
+	.aif_inv_sync							= REG_FIELD_ID(0x4004, 12, 12, 4, 0x1000),
+	.aif_sync_delay							= REG_FIELD_ID(0x4004, 8, 9, 4, 0x1000),
+	.aif_sync_mode							= REG_FIELD_ID(0x4004, 4, 5, 4, 0x1000),
+	.aif_sync_src							= REG_FIELD_ID(0x4004, 0, 0, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_BIT_WIDTH_CFG (0x4008 + 0x1000*a) */
+	.aif_sample_width_rx					= REG_FIELD_ID(0x4008, 24, 28, 4, 0x1000),
+	.aif_sample_width_tx					= REG_FIELD_ID(0x4008, 16, 20, 4, 0x1000),
+	.aif_slot_width_rx						= REG_FIELD_ID(0x4008, 8, 12, 4, 0x1000),
+	.aif_slot_width_tx						= REG_FIELD_ID(0x4008, 0, 4, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_FRAME_CFG (0x400C + 0x1000*a) */
+	.aif_bits_per_lane						= REG_FIELD_ID(0x400C, 0, 9, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_TX (0x4010 + 0x1000*a) */
+	.aif_slot_en_tx_mask					= REG_FIELD_ID(0x4010, 0, 31, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_RX (0x4030 + 0x1000*a) */
+	.aif_slot_en_rx_mask					= REG_FIELD_ID(0x4030, 0, 31, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_LANE_CFG (0x4050 + 0x1000*a) */
+	.aif_loopback_en						= REG_FIELD_ID(0x4050, 31, 31, 4, 0x1000),
+	.aif_ctrl_data_oe						= REG_FIELD_ID(0x4050, 16, 16, 4, 0x1000),
+	.aif_lane_en							= REG_FIELD_ID(0x4050, 8, 15, 4, 0x1000),
+	.aif_lane_dir							= REG_FIELD_ID(0x4050, 0, 7, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_MI2S_CFG (0x4054 + 0x1000*a) */
+	.aif_mono_mode_rx						= REG_FIELD_ID(0x4054, 1, 1, 4, 0x1000),
+	.aif_mono_mode_tx						= REG_FIELD_ID(0x4054, 0, 0, 4, 0x1000),
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_CFG (0x4058 + 0x1000*a) */
+	.aif_full_cycle_en						= REG_FIELD_ID(0x4058, 0, 0, 4, 0x1000),
+
+	.clk_name			= (const char*[]) {
+							"lpass_config_clk",
+							"lpass_core_axim_clk",
+							"bus_clk"
+						},
+	.num_clks			= 3,
+
+	.dai_driver			= shikra_qaif_cpu_dai_driver,
+	.num_dai			= ARRAY_SIZE(shikra_qaif_cpu_dai_driver),
+
+	.dai_osr_clk_names		= (const char *[]) {
+							"null"
+							},
+	.dai_bit_clk_names		= (const char *[]) {
+							"aif_if0_ibit_clk",
+							"aif_if1_ibit_clk",
+							"aif_if2_ibit_clk",
+							"aif_if3_ibit_clk"
+							},
+	.init					= shikra_qaif_init,
+	.exit					= shikra_qaif_exit,
+	.alloc_stream_dma_idx	= shikra_qaif_alloc_stream_dma_idx,
+	.free_stream_dma_idx	= shikra_qaif_free_stream_dma_idx,
+	.get_dma_idx			= shikra_qaif_get_dma_idx,
+};
+
+static const struct of_device_id shikra_qaif_cpu_device_id[] = {
+	{.compatible = "qcom,shikra-qaif-cpu", .data = &shikra_qaif_data},
+	{}
+};
+MODULE_DEVICE_TABLE(of, shikra_qaif_cpu_device_id);
+
+static struct platform_driver shikra_qaif_cpu_platform_driver = {
+	.driver = {
+		.name = "shikra-qaif-cpu",
+		.of_match_table = of_match_ptr(shikra_qaif_cpu_device_id),
+		.pm = &shikra_qaif_pm_ops,
+	},
+	.probe = asoc_qcom_qaif_cpu_platform_probe,
+	.remove = asoc_qcom_qaif_cpu_platform_remove,
+	.shutdown = asoc_qcom_qaif_cpu_platform_shutdown,
+};
+module_platform_driver(shikra_qaif_cpu_platform_driver);
+
+MODULE_DESCRIPTION("Qualcomm Audio Interface (QAIF) Shikra variant driver");
+MODULE_AUTHOR("Harendra Gautam <harendra.gautam@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
-- 
2.34.1


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

* Re: [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header
  2026-06-05 10:37 ` [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header Harendra Gautam
@ 2026-06-05 10:42   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-05 10:42 UTC (permalink / raw)
  To: Harendra Gautam, Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

On 05/06/2026 12:37, Harendra Gautam wrote:
> Add a dt-bindings header for the Qualcomm Audio Interface (QAIF) controller
> DAI IDs. This provides shared constants for devicetree sound-dai references
> and QAIF aif-interface reg values instead of using raw numeric IDs.
> 
> Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

This is is never a separate patch from the binding.

Best regards,
Krzysztof

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

* Re: [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding
  2026-06-05 10:37 ` [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding Harendra Gautam
@ 2026-06-05 10:46   ` Krzysztof Kozlowski
  2026-06-05 10:51     ` Krzysztof Kozlowski
  2026-06-05 11:27   ` Rob Herring (Arm)
  2026-06-05 11:45   ` sashiko-bot
  2 siblings, 1 reply; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-05 10:46 UTC (permalink / raw)
  To: Harendra Gautam, Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

On 05/06/2026 12:37, Harendra Gautam wrote:
> Add a Devicetree binding for the Qualcomm Audio Interface (QAIF) CPU DAI
> controller used on the Shikra audio platform.
> 
> QAIF moves PCM data between system memory and external serial audio
> interfaces through the AIF path, and between memory and the internal Bolero
> digital codec through the CIF path. The controller needs a binding so
> platform Devicetree files can describe its MMIO region, DMA IOMMU stream,
> clocks, interrupt, DAI cells and per-interface AIF configuration.
> 
> Describe the single register region, one EE interrupt, the required GCC
> LPASS and audio core clocks, the DMA IOMMU mapping, and 'aif-interface@N'
> child nodes used for static PCM, TDM or MI2S configuration.
> 

A nit, subject: drop second/last, redundant "bindings". The
"dt-bindings" prefix is already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters

> Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
> ---
>  .../devicetree/bindings/sound/qcom,qaif.yaml  | 353 ++++++++++++++++++
>  1 file changed, 353 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/qcom,qaif.yaml
> 
> diff --git a/Documentation/devicetree/bindings/sound/qcom,qaif.yaml b/Documentation/devicetree/bindings/sound/qcom,qaif.yaml
> new file mode 100644
> index 000000000000..5b385e05a650
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,qaif.yaml

Filename must match compatible.

> @@ -0,0 +1,361 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/qcom,qaif.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm Audio Interface (QAIF) CPU DAI Controller
> +
> +maintainers:
> +  - Harendra Gautam <harendra.gautam@oss.qualcomm.com>
> +
> +description:
> +  |
> +  The Qualcomm Audio Interface (QAIF) is a fully configurable DMA-based
> +  audio subsystem controller. It serialises and deserialises PCM audio
> +  between system memory and external serial audio peripherals (PCM, TDM,
> +  I2S, MI2S) through the AIF path, and transfers parallel audio between
> +  memory and an internal WCD codec through the CIF path.
> +
> +  AIF (Audio Interface): up to 13 multi-lane Unified Audio Interfaces,
> +  each supporting up to 8 independent data lanes. Each lane is individually
> +  configurable as TX (output/speaker) or RX (input/mic). All lanes of an
> +  interface share a single bit clock and frame sync. Supported modes are
> +  PCM (short/long sync), TDM, and MI2S (stereo/mono). Per-interface
> +  configuration includes sync source (master/slave), sync mode, sync delay,
> +  sync inversion, slot width (8/16/24/32-bit), sample width, active slot
> +  masks (up to 32 slots), bits-per-lane frame size, lane enable/direction
> +  masks, loopback, output-enable control, and full-cycle path support for
> +  long chip-to-chip connections.
> +
> +  CIF (Codec Interface): up to 32 RDDMA (playback) and 32 WRDMA (capture)
> +  channels connecting to an internal codec over a parallel bus. Each channel
> +  supports active-channel enable mask (up to 16 channels), frame-sync
> +  selection, frame-sync delay, frame-sync output gating, dynamic clock
> +  gating, and 16-bit packing/unpacking.
> +
> +  Note on RX/TX naming convention: in QAIF, RX refers to the capture path
> +  (audio received from the interface into memory) and TX refers to the
> +  playback path (audio transmitted from memory to the interface). This
> +  applies to both AIF lane directions and CIF slot/mask properties.
> +
> +  DMA engine: RDDMA fetches audio from DDR/TCM/LPM into a shared SRAM
> +  latency buffer (SHRAM) and drains it to the interface. WRDMA collects
> +  data from the interface into SHRAM and writes it to memory. Each DMA
> +  owns a private SHRAM region defined by start address and length registers.
> +  Burst sizes of 1/2/4/8/16 beats (64-bit) are supported with up to 4
> +  outstanding transactions per DMA. Two QSB master ports (QXM0 for TCM,
> +  QXM1 for DDR/LPM) provide the memory interface.
> +
> +  Resources are partitioned among up to 5 Execution Engines (EEs) via
> +  EE map registers. Each EE owns a set of DMAs, audio interfaces, and
> +  interface groups, and receives its own independent interrupt output.
> +  The interrupt hierarchy has a two-level structure: a summary register
> +  identifies the event class (DMA period, underflow/overflow, error
> +  response, audio interface underflow/overflow, group done, rate detector,
> +  VFR), and per-resource status registers identify the specific channel.
> +
> +  Interface grouping (bonding) allows up to 6 groups of audio and codec
> +  interfaces to start synchronously and align their DMA period interrupts
> +  within half a frame duration using the RDDMA padding feature.
> +
> +  Two rate detector blocks measure the frequency of incoming frame sync or
> +  word select signals and generate interrupts on rate change, undetected
> +  rate, or sync timeout.
> +
> +  Block diagram::
> +
> +    System Memory (DDR / LPM / TCM)
> +    +---------------------------------+
> +    |  Circular Buffers (ping-pong)   |
> +    +----------+----------+-----------+
> +               |          ^
> +         64-bit AXI  64-bit AXI
> +               |          |
> +    +----------v----------+-----------+
> +    |        QSB Master Ports         |
> +    |  +----------+  +----------+     |
> +    |  |   QXM0   |  |   QXM1   |     |
> +    |  +----+-----+  +-----+----+     |
> +    +-------|--------------|----------+
> +            |              |
> +    +-------v--------------v----------+
> +    |         Shared RAM (SHRAM)       |
> +    |  +------------+  +------------+ |
> +    |  | QXM0 Read  |  | QXM0 Write | |
> +    |  | SHRAM      |  | SHRAM      | |
> +    |  +------------+  +------------+ |
> +    |  +------------+  +------------+ |
> +    |  | QXM1 Read  |  | QXM1 Write | |
> +    |  | SHRAM      |  | SHRAM      | |
> +    |  +------------+  +------------+ |
> +    +---+--------+--------+-------+---+
> +        |        |        |       |
> +    +---v--+  +--v---+ +--v---+ +-v----+
> +    |RDDMA |  |RDDMA | |WRDMA | |WRDMA |
> +    | AIF  |  | CIF  | | AIF  | | CIF  |
> +    |[0..n]|  |[0..n]| |[0..n]| |[0..n]|
> +    +--+---+  +--+---+ +--+---+ +-+----+
> +       |         |       ^          ^
> +       | TX      | TX    | RX       | RX
> +       v         v       |          |
> +    +--+--------------------+  +----+----------+
> +    |  Unified Audio Intf   |  | Codec DMA     |
> +    |  (AIF 0..12)          |  | Interface     |
> +    |                       |  | (CIF)         |
> +    |  AUD_INTFa block:     |  |               |
> +    |  - Serializer (TX)    |  | RDDMA: DDR -> |
> +    |  - De-serializer (RX) |  |   internal    |
> +    |  - Sync gen/detect    |  |   codec       |
> +    |  - Up to 8 data lanes |  | WRDMA: codec  |
> +    |  - PCM / TDM / MI2S   |  |   -> DDR      |
> +    |  - Near Pad Logic     |  | Up to 16 ch   |
> +    +--+--------------------+  +----+----------+
> +       |  Lane 0..7 (TX/RX)       |  Parallel bus
> +       |  Bit clk + Frame sync    |  + Frame sync
> +       v                          v
> +    +--+--------+          +------+------+
> +    | External  |          | Internal    |
> +    | Serial    |          | Digital     |
> +    | Peripherals|         | Codec       |
> +    | (PCM/TDM/ |          | (Bolero/    |
> +    |  MI2S)    |          |  WCD)       |
> +    +-----------+          +-------------+
> +
> +properties:
> +  compatible:
> +    enum:
> +      - qcom,shikra-qaif-cpu
> +
> +  reg:
> +    maxItems: 1
> +
> +  iommus:
> +    maxItems: 1
> +
> +  clocks:
> +    minItems: 15

Drop

> +    maxItems: 15
> +
> +  clock-names:
> +    items:
> +      - const: lpass_config_clk
> +      - const: lpass_core_axim_clk
> +      - const: aud_dma_clk
> +      - const: aud_dma_mem_clk
> +      - const: bus_clk
> +      - const: aif_if0_ebit_clk
> +      - const: aif_if0_ibit_clk
> +      - const: aif_if1_ebit_clk
> +      - const: aif_if1_ibit_clk
> +      - const: aif_if2_ebit_clk
> +      - const: aif_if2_ibit_clk
> +      - const: aif_if3_ebit_clk
> +      - const: aif_if3_ibit_clk
> +      - const: ext_mclka_clk
> +      - const: ext_mclkb_clk

Drop _clk everywhere

> +
> +  interrupts:
> +    maxItems: 1
> +
> +  '#sound-dai-cells':
> +    const: 1
> +
> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 0
> +
> +  status: true

Please do not invent own style. Is there any binding with something like
that?

I finish here. Why? Because you sent DIFFERENT binding to internal
review. Then you made completely confusing changes and never reviewed it
internally again.

The internal review which Qualcomm implemented is for purpose of not
wasting community time on reviewing trivialities. You bypassing this and
sending us the trivialities feels like that wasting time is okay?

No, it is not okay.

Best regards,
Krzysztof

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

* Re: [PATCH 8/13] ASoC: qcom: Add generic of_xlate_dai_name helper to common
  2026-06-05 10:37 ` [PATCH 8/13] ASoC: qcom: Add generic of_xlate_dai_name helper to common Harendra Gautam
@ 2026-06-05 10:48   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 10:48 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: robh, devicetree, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Compilation failure due to conflicting static and non-static declarations of `asoc_qcom_of_xlate_dai_name`.
--

commit 49b02ad6fd90538b16f9ce1c7f6c4b2db0f7936f
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

ASoC: qcom: Add generic of_xlate_dai_name helper to common

Multiple Qualcomm ASoC CPU DAI drivers need to resolve a sound-dai phandle
argument to a DAI name. This commit extracts the common logic into
asoc_qcom_of_xlate_dai_name() in common.c so it can be shared.

[ ... ]
> diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
> index ee6662885593c..5baf51a39c975 100644
> --- a/sound/soc/qcom/common.h
> +++ b/sound/soc/qcom/common.h
[ ... ]
> @@ -15,5 +16,9 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
>  int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
>  			   struct snd_soc_jack *dp_jack, int id);
>  
> +int asoc_qcom_of_xlate_dai_name(const struct snd_soc_dai_driver *dai_drv,
> +				int num_dai,
> +				const struct of_phandle_args *args,
> +				const char **dai_name);
>  
>  #endif

[Severity: Low]
Does this new declaration break the build when included by lpass-cpu.c?

There is an existing static function in sound/soc/qcom/lpass-cpu.c with the
exact same name but a different signature:

static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component,
                                       const struct of_phandle_args *args,
                                       const char **dai_name)

Since lpass-cpu.c indirectly includes common.h, the compiler will see the
non-static declaration followed by the static definition, resulting in a
conflicting types error. 

While this is fixed later in the series by commit 761cbeeaa59f ("ASoC: qcom:
lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper"), could we avoid the
conflict in this patch to keep the commit history bisectable?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=8

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

* Re: [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding
  2026-06-05 10:46   ` Krzysztof Kozlowski
@ 2026-06-05 10:51     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-05 10:51 UTC (permalink / raw)
  To: Harendra Gautam, Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

On 05/06/2026 12:46, Krzysztof Kozlowski wrote:
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  '#sound-dai-cells':
>> +    const: 1
>> +
>> +  '#address-cells':
>> +    const: 1
>> +
>> +  '#size-cells':
>> +    const: 0
>> +
>> +  status: true
> 
> Please do not invent own style. Is there any binding with something like
> that?
> 
> I finish here. Why? Because you sent DIFFERENT binding to internal
> review. Then you made completely confusing changes and never reviewed it
> internally again.
> 
> The internal review which Qualcomm implemented is for purpose of not
> wasting community time on reviewing trivialities. You bypassing this and
> sending us the trivialities feels like that wasting time is okay?
> 

I stand corrected - you did send later version for internal review with
the status and toolset completely missed that.

Best regards,
Krzysztof

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

* Re: [PATCH 9/13] ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper
  2026-06-05 10:37 ` [PATCH 9/13] ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper Harendra Gautam
@ 2026-06-05 10:51   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 10:51 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The patch introduces an undefined reference linker error by using `asoc_qcom_of_xlate_dai_name` in `lpass-cpu.c` without ensuring that `CONFIG_SND_SOC_QCOM_COMMON` is selected in Kconfig.
--

commit 761cbeeaa59f428cdcde069e9ee4303793623ed7
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper
    
This commit replaces the private static asoc_qcom_of_xlate_dai_name()
implementation in lpass-cpu.c with a wrapper that calls the shared helper
from common.c.

> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index 242bc16da36da..35c2c80300242 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
[ ... ]
> @@ -458,30 +459,20 @@ const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2 = {
>  };
>  EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops2);
>  
> -static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component,
> +static int lpass_cpu_of_xlate_dai_name(struct snd_soc_component *component,
>  				   const struct of_phandle_args *args,
>  				   const char **dai_name)
>  {
>  	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
>  	const struct lpass_variant *variant = drvdata->variant;
> -	int id = args->args[0];
> -	int ret = -EINVAL;
> -	int i;
>  
> -	for (i = 0; i  < variant->num_dai; i++) {
> -		if (variant->dai_driver[i].id == id) {
> -			*dai_name = variant->dai_driver[i].name;
> -			ret = 0;
> -			break;
> -		}
> -	}
> -
> -	return ret;
> +	return asoc_qcom_of_xlate_dai_name(variant->dai_driver,
> +					   variant->num_dai, args, dai_name);

[Severity: Low]
Does this introduce a build failure if CONFIG_SND_SOC_QCOM_COMMON is not
enabled?

Because SND_SOC_LPASS_CPU does not appear to select SND_SOC_QCOM_COMMON in
sound/soc/qcom/Kconfig, using the shared helper here might lead to an
undefined reference linker error (such as modpost: asoc_qcom_of_xlate_dai_name
undefined) when the kernel is configured with only this driver.

>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=9

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

* Re: [PATCH 5/13] ASoC: qcom: Add QAIF shared data structures and variant interface
  2026-06-05 10:37 ` [PATCH 5/13] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
@ 2026-06-05 10:51   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 10:51 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: robh, devicetree, conor+dt

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Client status register bitmasks omit critical summary IRQ bits, which can lead to an unhandled interrupt storm.
- [High] The `qaif_drv_data` structure lacks a lock to serialize concurrent process accesses to shared mutable state, leading to data races and channel double-allocation.
- [Medium] The commit message explicitly claims to define 'QAIF-private MI2S port IDs used for the senary and septenary interfaces', but these definitions are missing from the patch.
- [Low] Generic identifiers `enum dma_type` and `DMA_TYPE_MAX` pollute the global subsystem namespace.
--

commit 54235d0e0d62830d345de2e4cee5111d43b55417
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

ASoC: qcom: Add QAIF shared data structures and variant interface

This commit introduces the qaif.h header, defining common data structures,
macros, and enums used by QAIF CPU DAI and PCM platform drivers. It includes
definitions for DMA control, IRQ mapping helpers, and the SOC variant
descriptor.

> Also define the QAIF-private MI2S port IDs used for the senary and
> septenary interfaces.

[Severity: Medium]
Are these MI2S port IDs missing from the patch? The commit message explicitly
claims to define QAIF-private MI2S port IDs for the senary and septenary
interfaces, but these definitions do not appear to be present in the provided
code.

> diff --git a/sound/soc/qcom/qaif.h b/sound/soc/qcom/qaif.h
> new file mode 100644
> index 0000000000000..d0f3b3026f5d9
> --- /dev/null
> +++ b/sound/soc/qcom/qaif.h

[ ... ]

> +enum qaif_dma_type {
> +	QAIF_AIF_DMA = 0,
> +	QAIF_CIF_DMA = 1,
> +	DMA_TYPE_MAX
> +};

[Severity: Low]
Does DMA_TYPE_MAX overlap confusingly with enum dma_type? It is defined inside
enum qaif_dma_type instead of enum dma_type.

[ ... ]

> +struct qaif_drv_data {
> +
> +	/* MI2S bit clock (derived from system clock by a divider) */
> +	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
> +
> +	/* SOC specific clock list */
> +	struct clk_bulk_data *clks;
> +	int num_clks;
> +
> +	struct clk *aud_dma_clk;
> +	struct clk *aud_dma_mem_clk;
> +
> +	/* Qualcomm audio interface (QAIF) registers */
> +	void __iomem *audio_qaif;
> +
> +	/* regmap backed by the Qualcomm audio interface (QAIF) registers */
> +	struct regmap *audio_qaif_map;
> +
> +	/* interrupts from the Qualcomm audio interface (QAIF) */
> +	int audio_qaif_irq;
> +
> +	/* QAIF init config refcount*/
> +	unsigned int qaif_init_ref_cnt;

[Severity: High]
Does this structure lack a lock to serialize concurrent access? 

Multiple PCMs mapped to different DAIs can be opened simultaneously by
userspace. Since ALSA only serializes operations per PCM via the PCM open
mutex, this allows concurrent execution of operations like PCM open for
different PCMs.

Could this lead to data races and channel double-allocation if multiple streams
concurrently read and increment qaif_init_ref_cnt or modify the channel
tracking bitmaps below?

> +
> +	/* SOC specific variations in the QAIF IP integration */
> +	const struct qaif_variant *variant;
> +
> +	/* Runtime-allocated regmap field handles (populated at probe) */
> +	struct qaif_dmactl *aif_rd_dmactl;
> +	struct qaif_dmactl *aif_wr_dmactl;
> +	struct qaif_dmactl *cif_rd_dmactl;
> +	struct qaif_dmactl *cif_wr_dmactl;
> +	struct qaif_aud_intfctl *aif_intfctl;
> +	struct qaif_cdc_intfctl *cif_rddma_intfctl;
> +	struct qaif_cdc_intfctl *cif_wrdma_intfctl;
> +	struct qaif_aif_config aif_intf_cfg[QAIF_MAX_AIF_CFG_CNT];
> +
> +	/* bit map to keep track of dma idx allocations */
> +	unsigned long aif_dma_idx_bit_map;
> +	unsigned long cif_dma_idx_bit_map;

[ ... ]

> +enum qaif_summary_irq_bitmask {
> +	QAIF_SUMMARY_BITMASK_AIF_PERIOD_RDDMA		= BIT(0),
> +	QAIF_SUMMARY_BITMASK_AIF_UNDERFLOW_RDDMA	= BIT(1),
> +	QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_RDDMA		= BIT(2),
> +	QAIF_SUMMARY_BITMASK_AIF_PERIOD_WRDMA		= BIT(3),
> +	QAIF_SUMMARY_BITMASK_AIF_OVERFLOW_WRDMA		= BIT(4),
> +	QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_WRDMA		= BIT(5),
> +
> +	QAIF_SUMMARY_BITMASK_AUD_OVERFLOW			= BIT(6),
> +	QAIF_SUMMARY_BITMASK_AUD_UNDERFLOW			= BIT(7),
> +
> +	QAIF_SUMMARY_BITMASK_RATE_DET				= BIT(8),
> +	QAIF_SUMMARY_BITMASK_VFR					= BIT(9),
> +	QAIF_SUMMARY_BITMASK_GRP					= BIT(10),
> +	QAIF_SUMMARY_BITMASK_RDDMA_OVERLAP			= BIT(11),
> +	QAIF_SUMMARY_BITMASK_WRDMA_OVERLAP			= BIT(12),
> +	QAIF_SUMMARY_BITMASK_INTF_OVERLAP			= BIT(13),
> +	QAIF_SUMMARY_BITMASK_GRP_OVERLAP			= BIT(14),
> +
> +	QAIF_SUMMARY_BITMASK_CIF_OVERLAP_RDDMA		= BIT(15),
> +	QAIF_SUMMARY_BITMASK_CIF_OVERLAP_WRDMA		= BIT(17),
> +	QAIF_SUMMARY_BITMASK_CIF_PERIOD_RDDMA		= BIT(18),
> +	QAIF_SUMMARY_BITMASK_CIF_UNDERFLOW_RDDMA	= BIT(19),
> +	QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_RDDMA		= BIT(20),
> +	QAIF_SUMMARY_BITMASK_CIF_PERIOD_WRDMA		= BIT(24),
> +	QAIF_SUMMARY_BITMASK_CIF_OVERFLOW_WRDMA		= BIT(25),
> +	QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_WRDMA		= BIT(26)
> +
> +};
> +
> +enum qaif_client_status_register_bitmask_info {
> +	QAIF_BITMASK_GROUP_INF			= 0x400,
> +	QAIF_BITMASK_AIF_RDDMA_WRDMA	= 0x3F,
> +	QAIF_BITMASK_CIF_RDDMA_WRDMA	= 0x71c0000,
> +	QAIF_BITMASK_DP_RDDMA			= 0xe00000,
> +	QAIF_BITMASK_AUD_INF			= 0xC0,
> +};

[Severity: High]
Are critical summary IRQ bits missing from these routing masks? 

The client status register bitmasks appear to omit several bits defined in
enum qaif_summary_irq_bitmask, such as the overlap bits (e.g., bit 15
QAIF_SUMMARY_BITMASK_CIF_OVERLAP_RDDMA), RATE_DET (bit 8), and VFR (bit 9).

If the hardware asserts an omitted bit, could the top-level IRQ handler return
IRQ_NONE and fail to clear it? This might lead to an unhandled interrupt storm
where the kernel IRQ subsystem eventually disables the IRQ line, breaking all
QAIF audio streams.

> +
> +struct qaif_irq_map {
> +	int client_id;
> +	u32 mask;
> +	irqreturn_t (*client_irq_handler)(struct qaif_drv_data *drvdata, u32 irq_status);
> +};
> +
> +enum dma_type {
> +	DMA_TYPE_RDDMA,
> +	DMA_TYPE_WRDMA
> +};

[Severity: Low]
Could this generic enum name cause namespace collisions? Defining enum
dma_type with values like DMA_TYPE_RDDMA in a shared SOC header lacks a
subsystem-specific prefix like qaif_. This risks colliding with other headers
in the global namespace.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=5

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

* Re: [PATCH 3/13] MAINTAINERS: Add Qualcomm QAIF driver entry
  2026-06-05 10:37 ` [PATCH 3/13] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
@ 2026-06-05 10:52   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-05 10:52 UTC (permalink / raw)
  To: Harendra Gautam, Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

On 05/06/2026 12:37, Harendra Gautam wrote:
> Add MAINTAINERS coverage for the Qualcomm Audio Interface (QAIF) driver
> so changes to its devicetree binding, CPU DAI driver, and PCM platform
> driver are routed to the Qualcomm ASoC maintainers and lists.
> 
> Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
> ---
>  MAINTAINERS | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f877e5aaf2c7..b38f03680ef3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21707,6 +21707,16 @@ F:	sound/soc/codecs/wcd93*.*
>  F:	sound/soc/codecs/wsa88*.*
>  F:	sound/soc/qcom/
>  
> +QCOM AUDIO INTERFACE (QAIF) DRIVER
> +M:	Harendra Gautam <harendra.gautam@oss.qualcomm.com>
> +M:	Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
> +L:	linux-sound@vger.kernel.org
> +L:	linux-arm-msm@vger.kernel.org
> +S:	Supported
> +F:	Documentation/devicetree/bindings/sound/qcom,qaif.yaml
> +F:	include/dt-bindings/sound/qcom,qaif.h
> +F:	sound/soc/qcom/qaif*

No such file.

Best regards,
Krzysztof

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

* Re: [PATCH 7/13] ASoC: qcom: Add QAIF AIF DAI ops
  2026-06-05 10:37 ` [PATCH 7/13] ASoC: qcom: Add QAIF AIF " Harendra Gautam
@ 2026-06-05 10:52   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 10:52 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Trigger error handling masks hardware enable failures and lacks rollback for clock enable failures.
- [High] Shared AIF enable bit in loopback mode breaks concurrent streams.
- [Medium] The CPU DAI improperly configures the CODEC DAI directly with hardcoded TDM masks, ignoring dynamic configurations.
- [Medium] The MI2S bit clock is left enabled during system suspend and idle periods due to unbalanced clock management.
- [Low] The pointer `aif_intf_cfg` is assigned the address of an inline array element and will never be NULL, rendering the subsequent NULL check useless.
--

commit d50489b023d0ced9ef2bb2d6c594bbd9cf3884e8
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
Subject: ASoC: qcom: Add QAIF AIF DAI ops

Add the CPU DAI operations and regmap field setup needed to use the QAIF
AIF (MI2S) path. This allows QAIF to route playback through RDDMA channels
and capture through WRDMA channels to external serial audio devices.

> diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
> --- a/sound/soc/qcom/qaif-cpu.c
> +++ b/sound/soc/qcom/qaif-cpu.c

[ ... ]

> @@ -495,12 +495,12 @@ static int qaif_aif_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
>  
>  	idx = v->get_dma_idx(dai->driver->id);
>  
>  	if (idx < 0) {
>  		dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
>  		return -EINVAL;
>  	}
>  
>  	aif_intf_cfg = &drvdata->aif_intf_cfg[idx];
>  
>  	if (!aif_intf_cfg) {
>  		dev_err(dai->dev, "AIF interface config not found\n");
>  		return -EINVAL;
>  	}

[Severity: Low]
Does this code assign the address of an inline array element? Since it takes
the address directly from the drvdata structure, won't this pointer never be
NULL, making the following check dead code?

[ ... ]

> @@ -603,17 +603,17 @@ static int qaif_aif_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
>  	ret = clk_set_rate(drvdata->mi2s_bit_clk[idx],
>  			   rate * slot_width * aif_intf_cfg->bits_per_lane);
>  	if (ret) {
>  		dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
>  			rate * slot_width * aif_intf_cfg->bits_per_lane, ret);
>  		return ret;
>  	}
>  	dev_dbg(dai->dev, "setting IBIT clock to %u\n",
>  		rate * slot_width * aif_intf_cfg->bits_per_lane);
>  
>  	ret = clk_enable(drvdata->mi2s_bit_clk[idx]);

[Severity: Medium]
Since this clock is enabled here and also in the trigger callback for
SNDRV_PCM_TRIGGER_START, does this double enable sequence mean the reference
count stays elevated during system suspend? ALSA does not call hw_free on
suspend, so it seems the clock might physically remain active.

>  	if (ret) {
>  		dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
>  		return ret;
>  	}
>  	snd_soc_dai_set_tdm_slot(codec_dai, 0x0f, 0b11, aif_intf_cfg->bits_per_lane, slot_width);
>  	snd_soc_dai_set_sysclk(codec_dai, 0, rate * aif_intf_cfg->bits_per_lane * slot_width, 0);

[Severity: Medium]
Is it intentional for a CPU DAI's hw_params to configure the codec DAI
directly with hardcoded values? This seems to force a mismatch by overriding
dynamic configurations like slot_en_tx_mask and tightly couples the CPU
driver to a specific codec.

[ ... ]

> @@ -638,28 +638,28 @@ static int qaif_aif_cpu_daiops_trigger(struct snd_pcm_substream *substream,
>  	case SNDRV_PCM_TRIGGER_START:
>  	case SNDRV_PCM_TRIGGER_RESUME:
>  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>  		if (aif_intf_cfg->loopback_en)
>  			ret = regmap_fields_write(drvdata->aif_intfctl->enable, idx, QAIF_AIF_CTL_ENABLE_ON);
>  		else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>  			ret = regmap_fields_write(drvdata->aif_intfctl->enable_tx, idx, QAIF_AIF_CTL_ENABLE_ON);
>  		else
>  			ret = regmap_fields_write(drvdata->aif_intfctl->enable_rx, idx, QAIF_AIF_CTL_ENABLE_ON);
>  		if (ret)
>  			dev_err(dai->dev, "error writing to AIF CTL reg: %d\n", ret);
>  
>  		ret = clk_enable(drvdata->mi2s_bit_clk[idx]);

[Severity: High]
If the regmap_fields_write call fails, won't the error code in ret be
overwritten by clk_enable here? Also, if clk_enable fails, it looks like
the function returns an error but leaves the AIF interface active without
rolling back the hardware state.

>  		if (ret) {
>  			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
>  			return ret;
>  		}
>  		break;
>  	case SNDRV_PCM_TRIGGER_STOP:
>  	case SNDRV_PCM_TRIGGER_SUSPEND:
>  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>  
>  		if (aif_intf_cfg->loopback_en)
>  			ret = regmap_fields_write(drvdata->aif_intfctl->enable, idx, QAIF_AIF_CTL_ENABLE_OFF);

[Severity: High]
If loopback is enabled and a user stops one substream during full-duplex
operation, won't writing QAIF_AIF_CTL_ENABLE_OFF to the global enable
register disrupt the other running stream? Should this global register state
be reference-counted to support concurrent streams?

>  		else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>  			ret = regmap_fields_write(drvdata->aif_intfctl->enable_tx, idx, QAIF_AIF_CTL_ENABLE_OFF);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=7

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

* Re: [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support
  2026-06-05 10:37 ` [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
@ 2026-06-05 10:58   ` Krzysztof Kozlowski
  2026-06-05 11:02   ` sashiko-bot
  1 sibling, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-05 10:58 UTC (permalink / raw)
  To: Harendra Gautam, Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

On 05/06/2026 12:37, Harendra Gautam wrote:
> diff --git a/sound/soc/qcom/qaif-shikra.c b/sound/soc/qcom/qaif-shikra.c
> new file mode 100644
> index 000000000000..e83564503087
> --- /dev/null
> +++ b/sound/soc/qcom/qaif-shikra.c
> @@ -0,0 +1,585 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + * qaif-shikra.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF
> + */
> +
> +#include <linux/module.h>
> +#include <sound/pcm.h>
> +#include <sound/soc.h>
> +#include <linux/pm.h>
> +#include "qaif.h"
> +
> +struct qaif_dmaidx_dai_map shikra_aif_dma_dai_map[] = {

Why is this global? And why not const?

I have doubts this passes builds without warnings.

Please run standard kernel tools for static analysis, like coccinelle,
smatch and sparse, and fix reported warnings. Also please check for
warnings when building with W=1 for gcc and clang. Most of these
commands (checks or W=1 build) can build specific targets, like some
directory, to narrow the scope to only your code. The code here looks
like it needs a fix. Feel free to get in touch if the warning is not clear.

> +		{ QAIF_MI2S_TDM_AIF0 },
> +		{ QAIF_MI2S_TDM_AIF1 },
> +		{ QAIF_MI2S_TDM_AIF2 },
> +		{ QAIF_MI2S_TDM_AIF3 }
> +};
> +

...

> +
> +static int shikra_qaif_init(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
> +	const struct qaif_variant *v = drvdata->variant;
> +	struct device *dev = &pdev->dev;
> +	int ret, i;
> +
> +	if (!v) {
> +		dev_err(dev, "No variant data\n");
> +		return -EINVAL;
> +	}
> +	if (v->num_clks == 0 || v->num_clks > QAIF_MAX_VARIANT_CLKS) {
> +		dev_err(dev, "Invalid clock count: %d\n", v->num_clks);
> +		return -EINVAL;
> +	}
> +	drvdata->clks = devm_kcalloc(dev, v->num_clks,
> +				     sizeof(*drvdata->clks), GFP_KERNEL);
> +	if (!drvdata->clks)
> +		return -ENOMEM;
> +
> +	drvdata->num_clks = v->num_clks;
> +
> +	for (i = 0; i < drvdata->num_clks; i++)
> +		drvdata->clks[i].id = v->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);

Is this probe path? If so, use dev_err_probe - please look at other
drivers how they are written.

Also, probe calls should be obvious, so often suffixed  "probe" not
"init". But maybe it's not probe path...

> +		return ret;
> +	}
> +
> +	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
> +	if (ret) {
> +		dev_err(dev, "shikra clk_enable failed\n");
> +		return ret;
> +	}
> +


...

> +	/* AUDIO_CORE_QAIF_AUD_INTFa_BIT_WIDTH_CFG (0x4008 + 0x1000*a) */
> +	.aif_sample_width_rx					= REG_FIELD_ID(0x4008, 24, 28, 4, 0x1000),
> +	.aif_sample_width_tx					= REG_FIELD_ID(0x4008, 16, 20, 4, 0x1000),
> +	.aif_slot_width_rx						= REG_FIELD_ID(0x4008, 8, 12, 4, 0x1000),
> +	.aif_slot_width_tx						= REG_FIELD_ID(0x4008, 0, 4, 4, 0x1000),
> +
> +	/* AUDIO_CORE_QAIF_AUD_INTFa_FRAME_CFG (0x400C + 0x1000*a) */
> +	.aif_bits_per_lane						= REG_FIELD_ID(0x400C, 0, 9, 4, 0x1000),
> +
> +	/* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_TX (0x4010 + 0x1000*a) */
> +	.aif_slot_en_tx_mask					= REG_FIELD_ID(0x4010, 0, 31, 4, 0x1000),
> +
> +	/* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_RX (0x4030 + 0x1000*a) */
> +	.aif_slot_en_rx_mask					= REG_FIELD_ID(0x4030, 0, 31, 4, 0x1000),
> +
> +	/* AUDIO_CORE_QAIF_AUD_INTFa_LANE_CFG (0x4050 + 0x1000*a) */
> +	.aif_loopback_en						= REG_FIELD_ID(0x4050, 31, 31, 4, 0x1000),
> +	.aif_ctrl_data_oe						= REG_FIELD_ID(0x4050, 16, 16, 4, 0x1000),
> +	.aif_lane_en							= REG_FIELD_ID(0x4050, 8, 15, 4, 0x1000),
> +	.aif_lane_dir							= REG_FIELD_ID(0x4050, 0, 7, 4, 0x1000),
> +
> +	/* AUDIO_CORE_QAIF_AUD_INTFa_MI2S_CFG (0x4054 + 0x1000*a) */
> +	.aif_mono_mode_rx						= REG_FIELD_ID(0x4054, 1, 1, 4, 0x1000),
> +	.aif_mono_mode_tx						= REG_FIELD_ID(0x4054, 0, 0, 4, 0x1000),
> +
> +	/* AUDIO_CORE_QAIF_AUD_INTFa_CFG (0x4058 + 0x1000*a) */
> +	.aif_full_cycle_en						= REG_FIELD_ID(0x4058, 0, 0, 4, 0x1000),


Honestly, I find everything above completely unreadable.

> +
> +	.clk_name			= (const char*[]) {
> +							"lpass_config_clk",
> +							"lpass_core_axim_clk",
> +							"bus_clk"
> +						},

Same here.

> +	.num_clks			= 3,
> +
> +	.dai_driver			= shikra_qaif_cpu_dai_driver,
> +	.num_dai			= ARRAY_SIZE(shikra_qaif_cpu_dai_driver),
> +
> +	.dai_osr_clk_names		= (const char *[]) {
> +							"null"
> +							},
> +	.dai_bit_clk_names		= (const char *[]) {
> +							"aif_if0_ibit_clk",
> +							"aif_if1_ibit_clk",
> +							"aif_if2_ibit_clk",
> +							"aif_if3_ibit_clk"
> +							},
> +	.init					= shikra_qaif_init,
> +	.exit					= shikra_qaif_exit,
> +	.alloc_stream_dma_idx	= shikra_qaif_alloc_stream_dma_idx,
> +	.free_stream_dma_idx	= shikra_qaif_free_stream_dma_idx,	
> +	.get_dma_idx			= shikra_qaif_get_dma_idx,
> +};
> +
> +static const struct of_device_id shikra_qaif_cpu_device_id[] = {
> +	{.compatible = "qcom,shikra-qaif-cpu", .data = &shikra_qaif_data},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, shikra_qaif_cpu_device_id);
> +
> +static struct platform_driver shikra_qaif_cpu_platform_driver = {
> +	.driver = {
> +		.name = "shikra-qaif-cpu",
> +		.of_match_table = of_match_ptr(shikra_qaif_cpu_device_id),

You have warnings here. Drop of_match_ptr.

> +		.pm = &shikra_qaif_pm_ops,
> +	},
> +	.probe = asoc_qcom_qaif_cpu_platform_probe,
> +	.remove = asoc_qcom_qaif_cpu_platform_remove,
> +	.shutdown = asoc_qcom_qaif_cpu_platform_shutdown,
> +};
> +module_platform_driver(shikra_qaif_cpu_platform_driver);
> +
> +MODULE_DESCRIPTION("Qualcomm Audio Interface (QAIF) Shikra variant driver");
> +MODULE_AUTHOR("Harendra Gautam <harendra.gautam@oss.qualcomm.com>");
> +MODULE_LICENSE("GPL");


Best regards,
Krzysztof

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

* Re: [PATCH 11/13] ASoC: qcom: Add QAIF PCM operations
  2026-06-05 10:37 ` [PATCH 11/13] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
@ 2026-06-05 11:02   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 11:02 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [Critical] Incorrect DMA buffer type `SNDRV_DMA_TYPE_CONTINUOUS` for `dma_alloc_coherent` memory leads to arbitrary physical memory mappings.
- [High] Missing `free_stream_dma_idx` cleanup in the `qaif_init` failure path.
- [High] The ALSA pointer callback returns raw negative error codes directly, corrupting the ALSA core pointer math.
- [High] Mismatched device structs between coherent DMA memory allocation and mapping for CIF streams.
- [High] Missing constraint to enforce period and buffer sizes as multiples of 8 bytes.
- [High] Concurrent `prepare` calls unconditionally overwrite global DMA clock rates.
- [Medium] Memory leak and uninitialized buffer values used for unrecognized DAIs.
- [Medium] Data race / TOCTOU on `qaif_init_ref_cnt` leading to multiple simultaneous hardware initializations.
- [Low] Bitwise OR operations are performed on negative error codes, scrambling the returned error values.
--

commit 282c91bcb4ac4fdd53a4566b6b00d7be3a79bcbc
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
Subject: ASoC: qcom: Add QAIF PCM operations

This commit adds PCM operation callbacks for the QAIF platform driver
to manage the DMA stream lifecycle for AIF and CIF DAIs.

> diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
> index b298ee9d865ee..beebec55222a5 100644
> --- a/sound/soc/qcom/qaif-platform.c
> +++ b/sound/soc/qcom/qaif-platform.c
> @@ -280,3 +280,570 @@ static int qaif_init(struct snd_soc_component *component)
[ ... ]
> +static int qaif_platform_pcmops_open(struct snd_soc_component *component,
> +				      struct snd_pcm_substream *substream)
> +{
[ ... ]
> +	buf = &substream->dma_buffer;
> +	buf->dev.dev = component->dev;
> +	buf->private_data = NULL;
> +	buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;

[Severity: Critical]
Does this code assign the wrong DMA buffer type?

Because the memory is allocated with dma_alloc_coherent() (via
qaif_mem_alloc_attach()), tagging it as SNDRV_DMA_TYPE_CONTINUOUS causes
ALSA's default mmap handler to assume the virtual address is linearly
mapped. The handler then attempts to use virt_to_page() to resolve
physical addresses, which can return incorrect page frames for vmalloc
or IOVA-backed addresses on ARM64. This could potentially allow
unprivileged userspace mapping of arbitrary physical memory.

[ ... ]
> +	ret = qaif_init(component);
> +	if (ret) {
> +		dev_err(soc_runtime->dev, "qaif_init failed: %d\n", ret);
> +		clk_disable_unprepare(drvdata->aud_dma_mem_clk);
> +		clk_disable_unprepare(drvdata->aud_dma_clk);
> +		qaif_mem_dealloc_detach(component->dev, dma_mem_info);
> +		kfree(data);
> +		return -EINVAL;
> +	}

[Severity: High]
Does this error path leak the DMA stream index?

The stream_dma_idx was dynamically allocated earlier, but it is not
released via v->free_stream_dma_idx() here. A few initialization
failures might exhaust the stream index pool and permanently break the
DAI until module reload.

> +	drvdata->qaif_init_ref_cnt++;

[Severity: Medium]
Could this refcount increment lead to a race condition?

The qaif_init() function checks if qaif_init_ref_cnt is zero before
executing the hardware setup. However, the increment happens here, after
qaif_init() has returned successfully. Since PCM devices have individual
locks, open() might be called simultaneously across different DAIs,
allowing multiple threads to see a zero refcount and simultaneously
execute hardware setup.

[ ... ]
> +	switch (dai_id) {
> +	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
[ ... ]
> +	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
[ ... ]
> +	default:
> +		break;
> +	}
> +
> +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);

[Severity: Medium]
Does this code proceed with uninitialized buffer values for unrecognized
DAIs?

The switch ignores unrecognized DAIs via the default branch. The function
then proceeds to configure the ALSA runtime buffer with an uninitialized
buf->bytes and buf->addr. The memory allocated for dma_mem_info earlier
in the function also appears to be leaked.

[ ... ]
> +static int qaif_platform_pcmops_prepare(struct snd_soc_component *component,
> +					 struct snd_pcm_substream *substream)
> +{
[ ... ]
> +	clk_set_rate(drvdata->aud_dma_clk, rate * bitwidth * channels * QAIF_DMA_CLK_RATE_MULTIPLIER);
> +	clk_set_rate(drvdata->aud_dma_mem_clk, rate * bitwidth * channels * QAIF_DMA_CLK_RATE_MULTIPLIER);

[Severity: High]
Can concurrent prepare calls break active streams by clobbering these
global clocks?

These global clocks provide the base frequency for the entire QAIF
hardware block. Because they are unconditionally overwritten here based
on the specific rate, channels, and bitwidth of the preparing stream,
concurrent streams with different formats might starve or break streams
that are already running.

[ ... ]
> +	ret = regmap_write(map, QAIF_DMABUFF_REG(v, idx, dir, dai_id),
> +			(snd_pcm_lib_buffer_bytes(substream) >> QAIF_DMA_BYTES_TO_WORDS_SHIFT) - 1);

[Severity: High]
Can this shift operation truncate buffer sizes and cause data corruption?

Because there is no ALSA constraint (like snd_pcm_hw_constraint_step)
enforcing an 8-byte alignment, userspace might request a size that is
not divisible by 8. QAIF_DMA_BYTES_TO_WORDS_SHIFT is 3, so the right
shift will silently truncate the remaining bytes. This makes the hardware
DMA buffer smaller than ALSA's software tracking expects, leading to
wrap-around mismatches.

[ ... ]
> +static int qaif_platform_irq_clear(struct qaif_drv_data *drvdata,
> +				int dir, enum qaif_irq_type irq_type, int idx)
> +{
> +	int ret = 0;
> +	const struct qaif_variant *v = drvdata->variant;
> +	struct regmap *map = drvdata->audio_qaif_map;
> +	unsigned int val_irqclr = BIT(idx);
> +
> +	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
> +		ret |= regmap_write(map, QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, irq_type), val_irqclr);
> +		ret |= regmap_write(map, QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, irq_type), val_irqclr);
> +		ret |= regmap_write(map, QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, irq_type), val_irqclr);

[Severity: Low]
Does this code inadvertently scramble error values?

The regmap_write() functions can return negative error codes. Two's
complement integers combined with bitwise OR operations will produce
incorrect error semantics or completely invalid codes, corrupting the
return value given to the caller.

[ ... ]
> +static snd_pcm_uframes_t qaif_platform_pcmops_pointer(
> +		struct snd_soc_component *component,
> +		struct snd_pcm_substream *substream)
> +{
[ ... ]
> +	idx = v->get_dma_idx(dai_id);
> +
> +	if (idx < 0) {
> +		dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx);
> +		return -EINVAL;
> +	}

[Severity: High]
Does this pointer callback return raw negative error codes?

The ALSA core pointer callback is defined to return snd_pcm_uframes_t
(unsigned long). A negative error code like -EINVAL will cast to an
enormous frame position, completely breaking ALSA's period math and
stream state. Consider returning SNDRV_PCM_POS_XRUN or 0 on error.

[ ... ]
> +static int qaif_platform_cdc_dma_mmap(struct snd_pcm_substream *substream,
> +				       struct vm_area_struct *vma)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +
> +	return dma_mmap_coherent(substream->pcm->card->dev, vma,
> +				 runtime->dma_area, runtime->dma_addr,
> +				 runtime->dma_bytes);
> +}

[Severity: High]
Is the wrong device pointer being used for this DMA mapping?

In qaif_mem_alloc_attach(), the memory is allocated using component->dev:

	dma_mem_info->vaddr = dma_alloc_coherent(dev, alloc_size,
						 &dma_mem_info->dma_addr,
						 GFP_KERNEL);

However, this mapping operation uses substream->pcm->card->dev. DMA API
constraints require mapping operations to use the exact same device
pointer used for memory allocation. If these devices belong to different
IOMMU domains, this could cause IOMMU faults or failed mmaps.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=11

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

* Re: [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support
  2026-06-05 10:37 ` [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
  2026-06-05 10:58   ` Krzysztof Kozlowski
@ 2026-06-05 11:02   ` sashiko-bot
  1 sibling, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 11:02 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: robh, devicetree, conor+dt

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [High] Core clocks are disabled in `remove()` before devres unregisters the ALSA component and IRQ handler, creating a race condition for bus crashes.
- [Low] DAI mapping arrays are missing the `static` keyword, polluting the global namespace.
- [Low] Latent build breakage risk due to `module_platform_driver()` within a variant file linked into a generic module.
- [Low] Using `of_match_ptr()` with an unconditionally defined match table triggers compiler warnings when `CONFIG_OF` is disabled.

Pre-existing issues:
- [High] Unprotected increment and decrement of the global reference counter `qaif_init_ref_cnt`.
--

commit 977cd466322e3f1bab9395ba1c325fb256edb595
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

ASoC: qcom: Add Shikra QAIF support

This commit adds the Shikra variant data for the Qualcomm Audio Interface
driver. It provides the register layout, DMA-to-DAI mappings, clocks, and
DAI descriptors required to enable playback or capture on Shikra platforms.
The new file is built as part of the snd-soc-qcom-qaif module.

[ ... ]

> diff --git a/sound/soc/qcom/qaif-shikra.c b/sound/soc/qcom/qaif-shikra.c
> new file mode 100644
> index 0000000000000..e835645030879
> --- /dev/null
> +++ b/sound/soc/qcom/qaif-shikra.c
> @@ -0,0 +1,585 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + * qaif-shikra.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF
> + */
> +
> +#include <linux/module.h>
> +#include <sound/pcm.h>
> +#include <sound/soc.h>
> +#include <linux/pm.h>
> +#include "qaif.h"
> +
> +struct qaif_dmaidx_dai_map shikra_aif_dma_dai_map[] = {
> +		{ QAIF_MI2S_TDM_AIF0 },
> +		{ QAIF_MI2S_TDM_AIF1 },
> +		{ QAIF_MI2S_TDM_AIF2 },
> +		{ QAIF_MI2S_TDM_AIF3 }
> +};
> +
> +struct qaif_dmaidx_dai_map shikra_cif_rx_dma_dai_map[] = {
> +		{ QAIF_CDC_DMA_RX0 },
> +		{ QAIF_CDC_DMA_RX1 },
> +		{ QAIF_CDC_DMA_RX2 },
> +		{ QAIF_CDC_DMA_RX3 }
> +};
> +
> +struct qaif_dmaidx_dai_map shikra_cif_tx_dma_dai_map[] = {
> +		{ QAIF_CDC_DMA_TX0 },
> +		{ QAIF_CDC_DMA_TX1 },
> +		{ QAIF_CDC_DMA_TX2 },
> +		{ QAIF_CDC_DMA_TX3 }
> +};
> +
> +struct qaif_dmaidx_dai_map shikra_cif_va_dma_dai_map[] = {
> +		{ QAIF_CDC_DMA_VA_TX0 },
> +		{ QAIF_CDC_DMA_VA_TX1 },
> +		{ QAIF_CDC_DMA_VA_TX2 },
> +		{ QAIF_CDC_DMA_VA_TX3 }
> +};

[Severity: Low]
Are these mapping arrays intended to be globally visible?

Since they appear to only be used locally in shikra_qaif_get_dma_idx(),
could they be marked static to prevent namespace pollution and avoid
sparse warnings?

[ ... ]

> +static int shikra_qaif_exit(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
> +
> +	if (!drvdata || !drvdata->clks)
> +		return -EINVAL;
> +
> +	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
> +	return 0;
> +}

[Severity: High]
Does disabling core clocks here create a race condition during teardown?

If shikra_qaif_exit() disables clocks directly from the remove() callback,
devm-managed resources like the QAIF IRQ handler and ALSA component will
still be active since devres cleanup happens after remove() completes.

If an IRQ fires or userspace accesses ALSA during this window, could
accessing the unclocked regmap trigger a synchronous external abort?

[ ... ]

> +static struct platform_driver shikra_qaif_cpu_platform_driver = {
> +	.driver = {
> +		.name = "shikra-qaif-cpu",
> +		.of_match_table = of_match_ptr(shikra_qaif_cpu_device_id),

[Severity: Low]
Could using of_match_ptr() here cause a compiler warning when CONFIG_OF is
disabled?

The shikra_qaif_cpu_device_id array is compiled unconditionally, so if
of_match_ptr() evaluates to NULL on non-OF builds, the array would be
left unreferenced.

> +		.pm = &shikra_qaif_pm_ops,
> +	},
> +	.probe = asoc_qcom_qaif_cpu_platform_probe,
> +	.remove = asoc_qcom_qaif_cpu_platform_remove,
> +	.shutdown = asoc_qcom_qaif_cpu_platform_shutdown,
> +};
> +module_platform_driver(shikra_qaif_cpu_platform_driver);

[Severity: Low]
Does using module_platform_driver() in a variant file cause issues for the
generic module?

Since snd-soc-qcom-qaif.ko links multiple object files, tying the module's
init and cleanup symbols directly to the Shikra variant might cause a multiple
definition build failure if another variant file is added later that also
calls module_platform_driver().

[Severity: High]
This is a pre-existing issue, but I noticed a potential problem in the
linked qaif-platform.c driver. When qaif_platform_pcmops_open() increments
the counter:

qaif_platform_pcmops_open()
    drvdata->qaif_init_ref_cnt++;

Since different DAIs map to different ALSA PCM streams, their open and
close callbacks can execute concurrently. Does this non-atomic increment
allow two concurrent opens to both read the counter as 0, race on the
hardware initialization, and corrupt the refcount?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=13

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

* Re: [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init
  2026-06-05 10:37 ` [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
@ 2026-06-05 11:02   ` Krzysztof Kozlowski
  2026-06-05 11:15   ` sashiko-bot
  1 sibling, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-05 11:02 UTC (permalink / raw)
  To: Harendra Gautam, Srinivas Kandagatla
  Cc: Mark Brown, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-sound, linux-arm-msm, devicetree,
	linux-kernel

On 05/06/2026 12:37, Harendra Gautam wrote:
> +int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata;
> +	struct resource *res;
> +	const struct qaif_variant *variant;
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +	int ret, i, dai_id, idx;
> +	bool variant_init_done = false;
> +
> +	dev_dbg(dev, "%s\n", __func__);

NAK, see further. This is clear, old known antipattern. Don't send such
code anymore.

> +	drvdata = devm_kzalloc(dev, sizeof(struct qaif_drv_data), GFP_KERNEL);

sizeof(*), and with third argument - lack of dev_err_probe - means you
just sent us old junk vendor code.

That's waste of the time - we will have to point you all standard
issues, we solved already years ago.

No. Drop all this code and start from current kernel code as your base
so you won't be repeating same old poor patterns.

> +	if (!drvdata)
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, drvdata);
> +
> +	match = of_match_device(dev->driver->of_match_table, dev);
> +	if (!match || !match->data)
> +		return -EINVAL;
> +
> +	drvdata->variant = (const struct qaif_variant *)match->data;

Why do you need to cast?

> +	variant = drvdata->variant;
> +	if (!variant) {
> +		dev_err(dev, "No variant data\n");

Is it a possible condition? Probably no, so why printing messages?

> +		return -EINVAL;
> +	}
> +
> +	ret = of_qaif_parse_aif_intf_cfg(dev, drvdata);
> +	if (ret) {
> +		dev_err(dev, "Failed to parse aif interfaces: %d\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	drvdata->audio_qaif =
> +			devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(drvdata->audio_qaif))
> +		return PTR_ERR(drvdata->audio_qaif);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -EINVAL;
> +
> +	audio_qaif_regmap_config.max_register = resource_size(res);
> +
> +	drvdata->audio_qaif_map = devm_regmap_init_mmio(dev, drvdata->audio_qaif,
> +				&audio_qaif_regmap_config);

Your code is barely readable.

> +	if (IS_ERR(drvdata->audio_qaif_map))
> +		return PTR_ERR(drvdata->audio_qaif_map);
> +
> +	ret = of_qaif_cdc_dma_clks_parse(dev, drvdata);
> +	if (ret) {
> +		dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (variant->init) {
> +		ret = variant->init(pdev);
> +		if (ret) {
> +			dev_err(dev, "error initializing variant: %d\n", ret);
> +			return ret;
> +		}
> +		variant_init_done = true;
> +	}
> +
> +	for (i = 0; i < variant->num_dai; i++) {
> +		dai_id = variant->dai_driver[i].id;
> +		if (is_cif_dma_port(dai_id))
> +			continue;
> +		idx = variant->get_dma_idx(dai_id);
> +		if (idx < 0)
> +			continue;
> +
> +		drvdata->mi2s_bit_clk[idx] = devm_clk_get(dev,
> +						variant->dai_bit_clk_names[idx]);
> +		if (IS_ERR(drvdata->mi2s_bit_clk[idx])) {
> +			dev_err(dev,
> +				"error getting %s: %ld\n",
> +				variant->dai_bit_clk_names[idx],
> +				PTR_ERR(drvdata->mi2s_bit_clk[idx]));
> +			ret = PTR_ERR(drvdata->mi2s_bit_clk[idx]);

Heh....

> +			goto err;
> +		}
> +	}
> +
> +	ret = qaif_aif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
> +	if (ret) {
> +		dev_err(dev, "error init cif bitfield: %d\n", ret);
> +		goto err;
> +	}
> +
> +	ret = qaif_aif_cfg_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
> +	if (ret) {
> +		dev_err(dev, "error init aif_intfctl field: %d\n", ret);
> +		goto err;
> +	}
> +
> +	ret = qaif_cif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
> +	if (ret) {
> +		dev_err(dev, "error init cif bitfield: %d\n", ret);
> +		goto err;
> +	}
> +
> +	ret = devm_snd_soc_register_component(dev, &qaif_cpu_comp_driver,
> +								variant->dai_driver,
> +								variant->num_dai);
> +	if (ret) {
> +		dev_err(dev, "error registering cpu driver: %d\n", ret);

Why aren't you using modern style? dev_err_probe?

> +		goto err;
> +	}
> +
> +	ret = asoc_qcom_qaif_platform_register(pdev);
> +	if (ret) {
> +		dev_err(dev, "error registering platform driver: %d\n", ret);
> +		goto err;
> +	}
> +	dev_dbg(&pdev->dev, "%s: QAIF CPU-Platform Driver Registered Successfully\n", __func__);

Drop. This does not look like useful printk message. Drivers should be
silent on success:
https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/coding-style.rst#L913
https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/debugging/driver_development_debugging_guide.rst#L79

Especially trivial probe successes are useless - core handles it.

> +err:
> +	if (ret && variant_init_done && variant->exit)
> +		variant->exit(pdev);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_probe);

Every exported function should have kerneldoc.

> +
> +void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
> +
> +	if (drvdata->variant->exit)
> +		drvdata->variant->exit(pdev);
> +}
> +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_remove);
> +




Best regards,
Krzysztof

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

* Re: [PATCH 12/13] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register
  2026-06-05 10:37 ` [PATCH 12/13] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
@ 2026-06-05 11:13   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 11:13 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:

New issues:
- [Critical] The IRQ handler accesses memory-mapped registers without ensuring that the required device clocks are enabled.
- [Critical] `qaif_platform_pcmops_resume` unconditionally attempts to write to hardware registers via `regcache_sync` while clocks are disabled.
- [High] Hardcoded DT parsing of the 'iommus' property makes the driver brittle and violates standard bindings.
- [High] Masking the interrupt clear register write leaves unhandled bits set, causing an infinite IRQ storm.
- [High] The DMA clocks are not properly balanced during system suspend/resume if multiple streams are open.
- [High] `snd_pcm_stop()` is called from the IRQ handler without holding the required ALSA stream lock.
- [High] Unprotected double-read of `substream[stream_dma_idx]` can lead to dereferencing a NULL pointer in the IRQ handler.
- [Medium] Clock enables are leaked if `regcache_sync()` fails during system resume.

Pre-existing issues:
- [High] The DMA memory is mapped to userspace with a different device pointer than the one used to allocate it.
- [High] Concurrent `.open` and `.close` operations lack locking, leading to data races on `qaif_init_ref_cnt`.
--

commit 79f7f868b0ea8024295676f54c8ca826ce51828f
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register

Complete the QAIF PCM platform support by registering the component,
requesting the QAIF interrupt, and handling DMA interrupt events.

> diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
> index beebec55222a5..8cf4d68f72b1c 100644
> --- a/sound/soc/qcom/qaif-platform.c
> +++ b/sound/soc/qcom/qaif-platform.c
> @@ -847,3 +847,323 @@ static int qaif_platform_pcmops_mmap(struct snd_soc_component *component,
>  
>  	return snd_pcm_lib_default_mmap(substream, vma);
>  }

[Severity: High]
This is a pre-existing issue, but does qaif_platform_cdc_dma_mmap map the
DMA buffer using the correct device pointer?

The buffer appears to be allocated using dma_alloc_coherent() with the
component device pointer, but qaif_platform_cdc_dma_mmap() maps it using:

return dma_mmap_coherent(substream->pcm->card->dev, vma, ...);

Could passing a different device pointer for mapping violate the DMA API
and cause IOMMU faults on platforms where only the component device is
attached to the SMMU domain?

> +
> +static irqreturn_t qaif_process_dma_irq(struct qaif_drv_data *drvdata,
[ ... ]
> +	mask = GENMASK(num_dma-1, 0);
> +	rv = regmap_read(drvdata->audio_qaif_map, stat_reg_addr, &reg);
> +	if (rv) {
> +		dev_err(regmap_get_device(drvdata->audio_qaif_map),
> +			"error reading stat reg 0x%x: %d\n", stat_reg_addr, rv);
> +		return IRQ_NONE;
> +	}
> +
> +	regmap_write(drvdata->audio_qaif_map, clr_reg_addr, reg & mask);

[Severity: High]
If the hardware asserts bits outside of the computed mask, will those bits
remain uncleared?

Because the IRQ is level-triggered (IRQF_TRIGGER_HIGH), could failing to
clear all asserted bits cause the interrupt controller to immediately
re-invoke the handler, leading to an interrupt storm and CPU lockup?

> +
> +	for (dma_idx = 0; dma_idx < num_dma; dma_idx++) {
> +		stream_dma_idx = dma_idx + stream_offset;
> +		if ((reg & BIT(dma_idx)) && substream[stream_dma_idx]) {
> +			stream = substream[stream_dma_idx];

[Severity: High]
Is there a risk of a null pointer dereference here due to a time-of-check
to time-of-use race?

If a concurrent close operation locklessly nullifies the substream array
entry between checking substream[stream_dma_idx] and assigning it to stream:

CPU1 (close)
drvdata->aif_substream[data->stream_dma_idx] = NULL;

CPU2 (qaif_process_dma_irq)
if ((reg & BIT(dma_idx)) && substream[stream_dma_idx]) {
    stream = substream[stream_dma_idx];

Would the handler then pass a NULL stream pointer to snd_pcm_stop()?
Should this use READ_ONCE() or be protected by a lock?

> +			switch (irq) {
> +			case QAIF_IRQ_PERIOD:
> +				snd_pcm_period_elapsed(stream);
> +				ret = IRQ_HANDLED;
> +				break;
> +
> +			case QAIF_IRQ_OVERFLOW:
> +			case QAIF_IRQ_UNDERFLOW:
> +				dev_warn_ratelimited(regmap_get_device(drvdata->audio_qaif_map),
> +						     "QAIF DMA xRun\n");
> +				ret = IRQ_HANDLED;
> +				break;
> +
> +			case QAIF_IRQ_ERROR:
> +				snd_pcm_stop(stream, SNDRV_PCM_STATE_DISCONNECTED);

[Severity: High]
Can this call to snd_pcm_stop() corrupt the PCM state?

The ALSA core API snd_pcm_stop() manipulates the substream's runtime state
and expects the caller to hold the PCM stream lock.  Could calling it
locklessly from this interrupt handler introduce a data race with
concurrent userspace or ALSA core operations?

Would using the IRQ-safe snd_pcm_stop_xrun() or manually acquiring the lock
be more appropriate here?

[ ... ]
> +static irqreturn_t asoc_platform_qaif_irq(int irq, void *data)
> +{
> +	struct qaif_drv_data *drvdata = data;
> +	const struct qaif_variant *v = drvdata->variant;
> +	u32 summary_irq_status;
> +	int rv, client;
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	rv = regmap_read(drvdata->audio_qaif_map,
> +			QAIF_SUMMARY_IRQSTAT_REG(v), &summary_irq_status);

[Severity: Critical]
Is it possible for this MMIO read to trigger a synchronous external abort?

If an interrupt fires (e.g. spurious or shared line activity) while no PCM
streams are active (qaif_init_ref_cnt == 0), the hardware clocks might be
physically disabled.

Since the handler unconditionally executes regmap_read() on
QAIF_SUMMARY_IRQSTAT_REG, could this unclocked access cause a bus stall
and kernel panic?

[ ... ]
> +static int qaif_platform_pcmops_suspend(struct snd_soc_component *component)
> +{
> +	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
> +	struct regmap *map;
> +
> +	map = drvdata->audio_qaif_map;
> +
> +	regcache_cache_only(map, true);
> +	regcache_mark_dirty(map);
> +	if (drvdata->qaif_init_ref_cnt > 0) {
> +		clk_disable(drvdata->aud_dma_clk);
> +		clk_disable(drvdata->aud_dma_mem_clk);
> +	}

[Severity: High]
Does this leave the hardware clocks running if multiple streams are active?

If clk_prepare_enable() is called for every new stream, the clock's
reference count is incremented N times for N active streams.

However, qaif_platform_pcmops_suspend() only calls clk_disable() once if
qaif_init_ref_cnt > 0.  If more than one stream was active, wouldn't the
enable count remain at N - 1, keeping the physical hardware clocks running
throughout system suspend?

> +	return 0;
> +}
> +
> +static int qaif_platform_pcmops_resume(struct snd_soc_component *component)
> +{
> +	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
> +	struct regmap *map = drvdata->audio_qaif_map;
> +	int ret;
> +
> +	if (drvdata->qaif_init_ref_cnt > 0) {
> +		ret = clk_enable(drvdata->aud_dma_clk);
> +		if (ret)
> +			return ret;
> +		ret = clk_enable(drvdata->aud_dma_mem_clk);
> +		if (ret) {
> +			clk_disable(drvdata->aud_dma_clk);
> +			return ret;
> +		}
> +	}
> +	regcache_cache_only(map, false);
> +	return regcache_sync(map);

[Severity: Critical]
Can this regcache_sync() call cause an unclocked access exception?

If no streams were active before suspend (qaif_init_ref_cnt == 0), the
driver skips enabling the clocks.

Because the system suspend callback executed regcache_mark_dirty(map), this
unconditional regcache_sync() attempt will write to the hardware registers.
Could writing to the MMIO registers of a clock-gated IP block cause an
SError and system crash?

[Severity: Medium]
Does this leak the clock enables if regcache_sync() fails?

If regcache_sync(map) returns an error, it fails to call clk_disable() on
the previously enabled aud_dma_clk and aud_dma_mem_clk.

Could transient regmap failures during successive suspend/resume cycles
continuously increment the clock reference counts and prevent the clocks
from ever being turned off?

[ ... ]
> +static const struct snd_soc_component_driver qaif_component_driver = {
> +	.name		= DRV_NAME,
> +	.open		= qaif_platform_pcmops_open,

[Severity: High]
This is a pre-existing issue, but are concurrent calls to
qaif_platform_pcmops_open() and qaif_platform_pcmops_close() safe?

The global drvdata->qaif_init_ref_cnt is modified locklessly in these
functions. Since a single component can expose multiple DAIs and
independent PCMs, could concurrent open and close calls for different PCMs
race on the read-modify-write operations and corrupt the counter?

Could this lead to premature clock disablement during active playback?

[ ... ]
> +static int qaif_parse_smmu_sid(struct platform_device *pdev,
> +				struct qaif_drv_data *drvdata)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	int ret;
> +	u32 sid;
> +
> +	ret = of_property_read_u32_index(np, "iommus", 1, &sid);

[Severity: High]
Does hardcoding the DT parsing of the "iommus" property break probe on
platforms with different IOMMU bindings?

By bypassing standard OF parsing APIs (like of_parse_phandle_with_args)
and fetching the SMMU SID at index 1 directly, this assumes a specific
layout.

What happens on platforms where #iommu-cells = <2> (where index 1 might be
the ID and index 2 the mask, or index 0 is phandle), or on platforms
without an IOMMU? Will this return a fatal error and completely prevent
audio initialization?

> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to read SMMU SID from iommus property: %d\n", ret);
> +		return ret;
> +	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=12

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

* Re: [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init
  2026-06-05 10:37 ` [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
  2026-06-05 11:02   ` Krzysztof Kozlowski
@ 2026-06-05 11:15   ` sashiko-bot
  1 sibling, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 11:15 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] Missing lock protection around `qaif_init_ref_cnt` leads to un-serialized hardware initialization data race.
- [High] Using `GENMASK` with unchecked channel counts (e.g., `v->num_rddma - 1`) causes integer underflow and undefined behavior if the count is zero.
- [High] Component teardown order is inverted, shutting down hardware before devm-managed ALSA components are unregistered.
- [High] Modifying the global static `regmap_config` structure during probe causes race conditions and an out-of-bounds register access bug.
- [High] The error code from enabling the AIF CTL register is overwritten without being returned, allowing a stream to silently fail to start.
- [High] A missing `qcom,qaif-aif-bits-per-lane` property defaults to 0, causing underflows during regmap writes and setting the DAI bitclock to 0Hz.
- [Medium] Variable `intf_idx` is declared as an unsigned 32-bit integer, making the error check `if (intf_idx < 0)` tautologically false.
- [Medium] Accumulating negative error codes using bitwise OR (`ret |= regmap_write(...)`) corrupts the return value.
--

commit 84915833d4dcf01d21e4e1a894c18953827f441d
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

    ASoC: qcom: Add QAIF regmap, DT parsing and platform init
    
    Add the core CPU and platform bring-up needed for the Qualcomm Audio
    Interface driver.
    
    The existing QAIF DAI code needs device-level infrastructure before it can
    configure interfaces or run PCM streams. Add the MMIO regmap setup,
    including readable, writable and volatile register tables, so the driver
    can safely access QAIF control, DMA, interrupt and SHRAM registers.

> diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
> index c9c8d78c5f5f8..e1030f5b17524 100644
> --- a/sound/soc/qcom/qaif-cpu.c
> +++ b/sound/soc/qcom/qaif-cpu.c
> @@ -783,3 +783,804 @@ const struct snd_soc_dai_ops asoc_qcom_qaif_aif_cpu_dai_ops = {
>  	.trigger	= qaif_aif_cpu_daiops_trigger,
>  };

[Severity: High]
Does qaif_aif_cpu_daiops_trigger() overwrite the error code from enabling the
AIF CTL register?

In the SNDRV_PCM_TRIGGER_START path of qaif_aif_cpu_daiops_trigger(), the
driver calls regmap_fields_write() to turn on the interface. If this write
fails, it logs an error but proceeds to call clk_enable(), overwriting the
ret variable.

If the clock enables successfully, the trigger function returns 0 (success)
to the ALSA core. The framework and application will falsely assume the
stream is running, but the hardware interface remains disabled, resulting
in silent audio playback/capture failures.

[ ... ]
> +static int of_qaif_parse_aif_intf_cfg(struct device *dev,
> +					struct qaif_drv_data *data)
> +{
[ ... ]
> +		if (v->get_dma_idx) {
> +			intf_idx = v->get_dma_idx(dai_id);
> +			if (intf_idx < 0) {

[Severity: Medium]
Since intf_idx is declared as a u32, will this check for negative error codes
ever evaluate to true?

If v->get_dma_idx() returns a negative error code like -EINVAL, it wraps to a
large positive integer. The check intf_idx < 0 will always evaluate to false,
bypassing this error path. The error is only caught by the subsequent bounds
check, which will print a misleading error message instead of failing properly.

[ ... ]
> +		/* Parse frame configuration */
> +		cfg->full_cycle_en = of_property_read_bool(intf_np, "qcom,qaif-aif-full-cycle-en");
> +
> +		ret = of_property_read_u32(intf_np, "qcom,qaif-aif-bits-per-lane", &cfg->bits_per_lane);
> +		if (ret) {
> +			dev_warn(dev, "Missing bits-per-lane for interface %d\n", num_interfaces);
> +			cfg->bits_per_lane = 0;

[Severity: High]
Does assigning 0 as a default here cause an integer underflow later?

When this default value is used in qaif_aif_cpu_daiops_hw_params(), it causes
an integer underflow: (slot_width * 0) - 1 evaluates to 0xFFFFFFFF. This
illegal value is then written directly to the bits_per_lane hardware
register field. It also causes clk_set_rate() to request exactly 0Hz
for the IBIT clock.

[ ... ]
> +int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev)
> +{
[ ... ]
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -EINVAL;
> +
> +	audio_qaif_regmap_config.max_register = resource_size(res);

[Severity: High]
Does modifying this global static struct cause a data race if multiple
QAIF instances are probed concurrently?

Also, resource_size(res) defines the maximum inclusive valid register
address. Setting max_register exactly to the resource size permits regmap
accesses one byte beyond the valid mapped region.

> +
> +	drvdata->audio_qaif_map = devm_regmap_init_mmio(dev, drvdata->audio_qaif,
> +				&audio_qaif_regmap_config);
[ ... ]
> +void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
> +
> +	if (drvdata->variant->exit)
> +		drvdata->variant->exit(pdev);

[Severity: High]
Does shutting down the hardware here invert the component teardown order?

The driver registers sound components and IRQs using devm-managed functions,
meaning they are unregistered after the remove() callback returns. Manually
calling the exit() variant here disables the hardware clocks while the ALSA
component is still fully registered and accessible to userspace.

If an active stream or concurrent operation attempts unclocked regmap
accesses during this window, it could cause a synchronous external abort
or kernel panic.

> +}
[ ... ]

> diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
> new file mode 100644
> index 0000000000000..b298ee9d865ee
> --- /dev/null
> +++ b/sound/soc/qcom/qaif-platform.c
[ ... ]
> +static int qaif_map_ee_resource(struct qaif_drv_data *drvdata)
> +{
> +	const struct qaif_variant *v = drvdata->variant;
> +	struct regmap *map = drvdata->audio_qaif_map;
> +	int ret = 0;
> +	u32 mask;
> +
> +	mask = GENMASK(v->num_rddma - 1, 0);

[Severity: High]
If a hardware variant has 0 channels for a specific DMA type, does this cause
an integer underflow?

Calling GENMASK(0xFFFFFFFF, 0) invokes an out-of-bounds right shift in the
kernel's __GENMASK macro, which is undefined behavior. This can generate
invalid bitmasks and potentially grant access to nonexistent channels or
corrupt registers.

> +	ret |= regmap_write(map, QAIF_EE_RDDMA_MAP_REG(v), mask);
> +
> +	mask = GENMASK(v->num_wrdma - 1, 0);
> +	ret |= regmap_write(map, QAIF_EE_WRDMA_MAP_REG(v), mask);

[Severity: Medium]
Is it safe to accumulate negative error codes using bitwise OR?

regmap_write() returns a negative error code (e.g., -EINVAL) on failure.
If multiple writes fail, their bitwise combination can result in an
arbitrary and misleading error code, masking the true cause of the failure
and confusing upper layers.

[ ... ]
> +static int qaif_map_dma_path(struct qaif_drv_data *drvdata)
> +{
> +	struct regmap *map = drvdata->audio_qaif_map;
> +	const struct qaif_variant *v = drvdata->variant;
> +	int ret = 0;
> +	int qxm_sel = v->qxm_type;
> +
> +	if (qxm_sel != QXM0) {
> +		dev_err(regmap_get_device(map),
> +			"%s: only QXM0 is supported, qxm_type=%d\n",
> +			__func__, qxm_sel);
> +		return -EINVAL;
> +	}
> +
> +	ret |= regmap_write(map, QAIF_RDDMA_MAP_QXM, qxm_sel);
> +	ret |= regmap_write(map, QAIF_WRDMA_MAP_QXM, qxm_sel);

[Severity: Medium]
Could bitwise ORing the return values here corrupt standard error values?

If multiple regmap_write() calls return negative standard error codes,
combining them via bitwise OR will result in arbitrary and misleading error
values.

[ ... ]
> +static int qaif_init(struct snd_soc_component *component)
> +{
> +	struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
> +	int ret = 0;
> +
> +	if (drvdata->qaif_init_ref_cnt) {
> +		dev_dbg(component->dev, "%s: QAIF init is done already: ref cnt: %d\n",
> +				__func__, drvdata->qaif_init_ref_cnt);
> +		return 0;
> +	}

[Severity: High]
Is it possible for concurrent PCM opens to race on this lockless check?

qaif_init() checks qaif_init_ref_cnt without holding any locks. Its caller,
qaif_platform_pcmops_open(), calls qaif_init() and then increments the
counter. Since ALSA's pcm->open_mutex is per-PCM device, different DAIs
can be opened concurrently by userspace, causing a data race.

This could result in simultaneous writes to shared hardware registers and
corrupted hardware state.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=10

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

* Re: [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding
  2026-06-05 10:37 ` [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding Harendra Gautam
  2026-06-05 10:46   ` Krzysztof Kozlowski
@ 2026-06-05 11:27   ` Rob Herring (Arm)
  2026-06-05 11:45   ` sashiko-bot
  2 siblings, 0 replies; 30+ messages in thread
From: Rob Herring (Arm) @ 2026-06-05 11:27 UTC (permalink / raw)
  To: Harendra Gautam
  Cc: Conor Dooley, Srinivas Kandagatla, Krzysztof Kozlowski,
	devicetree, linux-sound, linux-arm-msm, linux-kernel, Mark Brown,
	Liam Girdwood


On Fri, 05 Jun 2026 16:07:28 +0530, Harendra Gautam wrote:
> Add a Devicetree binding for the Qualcomm Audio Interface (QAIF) CPU DAI
> controller used on the Shikra audio platform.
> 
> QAIF moves PCM data between system memory and external serial audio
> interfaces through the AIF path, and between memory and the internal Bolero
> digital codec through the CIF path. The controller needs a binding so
> platform Devicetree files can describe its MMIO region, DMA IOMMU stream,
> clocks, interrupt, DAI cells and per-interface AIF configuration.
> 
> Describe the single register region, one EE interrupt, the required GCC
> LPASS and audio core clocks, the DMA IOMMU mapping, and 'aif-interface@N'
> child nodes used for static PCM, TDM or MI2S configuration.
> 
> Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
> ---
>  .../devicetree/bindings/sound/qcom,qaif.yaml  | 353 ++++++++++++++++++
>  1 file changed, 353 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/qcom,qaif.yaml
> 

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

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/sound/qcom,qaif.example.dts:28:18: fatal error: dt-bindings/clock/qcom,shikra-audiocorecc.h: No such file or directory
   28 |         #include <dt-bindings/clock/qcom,shikra-audiocorecc.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [scripts/Makefile.dtbs:140: Documentation/devicetree/bindings/sound/qcom,qaif.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1662: dt_binding_check] Error 2
make: *** [Makefile:248: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.kernel.org/project/devicetree/patch/20260605103739.3557573-3-harendra.gautam@oss.qualcomm.com

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

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

pip3 install dtschema --upgrade

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


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

* Re: [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding
  2026-06-05 10:37 ` [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding Harendra Gautam
  2026-06-05 10:46   ` Krzysztof Kozlowski
  2026-06-05 11:27   ` Rob Herring (Arm)
@ 2026-06-05 11:45   ` sashiko-bot
  2 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-06-05 11:45 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The property `qcom,qaif-aif-bits-per-lane` directly contradicts its own description and example, creating severe ambiguity for device tree authors and driver implementations.
--

commit 156362ec3cefdde2ab7850355d1276f1ccef3348
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

dt-bindings: sound: Add Qualcomm QAIF binding

This commit adds a DeviceTree binding for the Qualcomm Audio Interface (QAIF)
CPU DAI controller used on the Shikra audio platform. It describes the MMIO
region, clocks, interrupts, DMA IOMMU mapping, and per-interface AIF child
nodes.

> diff --git a/Documentation/devicetree/bindings/sound/qcom,qaif.yaml b/Documentation/devicetree/bindings/sound/qcom,qaif.yaml
> new file mode 100644
> index 0000000000000..9c4371f13ff3b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,qaif.yaml

[ ... ]

> +      qcom,qaif-aif-full-cycle-en:
> +        type: boolean
> +        description: Enable full-cycle sync (effective in sync master mode).
> +      qcom,qaif-aif-bits-per-lane:
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +        description:
> +          Number of slots per lane. The frame length is computed as
> +          slot-width multiplied by bits-per-lane.

[Severity: Medium]
Does the property name qcom,qaif-aif-bits-per-lane contradict its intended
meaning?

The description defines it as the number of slots per lane and explains that
the frame length is computed by multiplying the slot-width (which is in bits)
by this value. 

If this property actually represents a dimensionless slot count, would it be
clearer to name it qcom,qaif-aif-slots-per-lane?

Naming a slot count property bits-per-lane might lead authors to incorrectly
configure it as a bit count instead of a slot count.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com?part=2

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

end of thread, other threads:[~2026-06-05 11:45 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 10:37 [PATCH 0/13] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
2026-06-05 10:37 ` [PATCH 1/13] dt-bindings: sound: Add Qualcomm QAIF DAI ID header Harendra Gautam
2026-06-05 10:42   ` Krzysztof Kozlowski
2026-06-05 10:37 ` [PATCH 2/13] dt-bindings: sound: Add Qualcomm QAIF binding Harendra Gautam
2026-06-05 10:46   ` Krzysztof Kozlowski
2026-06-05 10:51     ` Krzysztof Kozlowski
2026-06-05 11:27   ` Rob Herring (Arm)
2026-06-05 11:45   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 3/13] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
2026-06-05 10:52   ` Krzysztof Kozlowski
2026-06-05 10:37 ` [PATCH 4/13] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
2026-06-05 10:37 ` [PATCH 5/13] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
2026-06-05 10:51   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 6/13] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
2026-06-05 10:37 ` [PATCH 7/13] ASoC: qcom: Add QAIF AIF " Harendra Gautam
2026-06-05 10:52   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 8/13] ASoC: qcom: Add generic of_xlate_dai_name helper to common Harendra Gautam
2026-06-05 10:48   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 9/13] ASoC: qcom: lpass-cpu: Use asoc_qcom_of_xlate_dai_name helper Harendra Gautam
2026-06-05 10:51   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 10/13] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
2026-06-05 11:02   ` Krzysztof Kozlowski
2026-06-05 11:15   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 11/13] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
2026-06-05 11:02   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 12/13] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
2026-06-05 11:13   ` sashiko-bot
2026-06-05 10:37 ` [PATCH 13/13] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
2026-06-05 10:58   ` Krzysztof Kozlowski
2026-06-05 11:02   ` sashiko-bot

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