Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform
@ 2026-07-01 10:57 Harendra Gautam
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
                   ` (11 more replies)
  0 siblings, 12 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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.

Link to v1:
  https://lore.kernel.org/lkml/20260605103739.3557573-1-harendra.gautam@oss.qualcomm.com/
Changes in v2:
  - Rename YAML file to qcom,qaif-cpu.yaml, add qcom,qaif-cpu compatible
  - Merge DAI ID header into binding patch, drop _clk suffix from
    clock-names, add enum constraints, remove status: true (Krzysztof)
  - Fix dt_binding_check failure in binding example (Rob)
  - Fix IRQ storm: client status register bitmask missing BIT(16)
  - Fix race: use test_and_set_bit for DMA bitmap allocation
  - Fix regmap_config: use non-const with dynamic max_register
  - Use dev_err_probe() in probe path, sizeof(*var) in kzalloc (Krzysztof)
  - Add kerneldoc for all EXPORT_SYMBOL_GPL functions (Krzysztof)
  - Use goto-based error cleanup in pcmops_open()
  - Move MAINTAINERS entry to last patch (Krzysztof)
  - Add shared asoc_qcom_of_xlate_dai_name() helper used by lpass-cpu
    and qaif-cpu
  - Fix checkpatch --strict warnings and alignment issues

Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller
  support on Qualcomm Shikra SoC
  https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/

Harendra Gautam (11):
  dt-bindings: sound: Add Qualcomm QAIF binding
  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 and use it in
    lpass-cpu and qaif-cpu
  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
  MAINTAINERS: Add Qualcomm QAIF driver entry

 .../devicetree/bindings/sound/qcom,qaif-cpu.yaml   |  358 +++++
 MAINTAINERS                                   |   10 +
 include/dt-bindings/sound/qcom,qaif.h         |   89 ++
 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                    |   23 +-
 sound/soc/qcom/qaif-cpu.c                     | 1610 ++++++++++++++++++++
 sound/soc/qcom/qaif-platform.c                | 1250 +++++++++++++++
 sound/soc/qcom/qaif-reg.h                     |  453 ++++++
 sound/soc/qcom/qaif-shikra.c                  |  556 +++++++
 sound/soc/qcom/qaif.h                         |  506 ++++++
 13 files changed, 4891 insertions(+), 16 deletions(-)
 create mode 100644 .../devicetree/bindings/sound/qcom,qaif-cpu.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

base-commit: bebb6ea69b2a793e8c840d7c9c4f18cb1747570f
prerequisite-change-id: 20260605-shikra-audiocorecc-85efef073474
prerequisite-patch-id: 591dd358a559fa83d46149f74f80ff0f2a98da7f
prerequisite-patch-id: 4d40e704139dc4b0ec2529c49f096d86a4e4dc5f
prerequisite-patch-id: c02813140f0c1c3d783f5643e34ef8e175cb20ea
prerequisite-patch-id: b9dbd75b7919d7d2dd621194609507ede839b9bf
-- 
2.34.1


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

* [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:04   ` Konrad Dybcio
                     ` (4 more replies)
  2026-07-01 10:57 ` [PATCH v2 02/11] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
                   ` (10 subsequent siblings)
  11 siblings, 5 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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.

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.

Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller
  support on Qualcomm Shikra SoC
  https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/
Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 .../bindings/sound/qcom,qaif-cpu.yaml         | 358 ++++++++++++++++++
 include/dt-bindings/sound/qcom,qaif.h         |  92 +++++
 2 files changed, 447 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml
 create mode 100644 include/dt-bindings/sound/qcom,qaif.h

diff --git a/Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml
new file mode 100644
index 000000000000..6b1ced01e04f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml
@@ -0,0 +1,358 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,qaif-cpu.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,qaif-cpu
+      - qcom,shikra-qaif-cpu
+
+  reg:
+    maxItems: 1
+
+  iommus:
+    maxItems: 1
+
+  clocks:
+    maxItems: 9
+
+  clock-names:
+    items:
+      - const: lpass_config
+      - const: lpass_core_axim
+      - const: aud_dma
+      - const: aud_dma_mem
+      - const: bus
+      - const: aif_if0_ibit
+      - const: aif_if1_ibit
+      - const: aif_if2_ibit
+      - const: aif_if3_ibit
+
+  interrupts:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+
+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
+        enum: [0, 1, 2]
+        description:
+          Defines the FRAME SYNC mode for the audio interface.
+            0 = SHORT - FRAME SYNC is high for 1 INTF clock cycle per frame
+            1 = ONE_SLOT - FRAME SYNC is high for 1 slot per frame (pulse
+                stretched by MIN(RPCM_WIDTH, TPCM_WIDTH))
+            2 = LONG - FRAME SYNC is high for half the frame duration
+      qcom,qaif-aif-sync-src:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+        description:
+          Sync clock/frame source. Independent of sync-mode;
+          all combinations of sync-mode and sync-src are valid.
+            0 = External source (slave)
+            1 = Internal source (master)
+      qcom,qaif-aif-invert-sync:
+        type: boolean
+        description: Invert the frame sync polarity.
+      qcom,qaif-aif-sync-delay:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2]
+        description:
+          Defines MSB data start position relative to FRAME SYNC edge.
+            0 = MSB data starts at the rising/falling edge of FRAME SYNC
+            1 = MSB data starts 1 INTF clock cycle after FRAME SYNC edge
+            2 = MSB data starts 2 INTF clock cycles after FRAME SYNC edge
+      qcom,qaif-aif-slot-width-rx:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [8, 16, 24, 32]
+        description:
+          RX slot width in bits. 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
+        enum: [8, 16, 24, 32]
+        description:
+          TX slot width in bits. 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 DMA and interface-level loopback mode.
+          Used for hardware validation only; omit in production.
+      qcom,qaif-aif-ctrl-data-oe:
+        type: boolean
+        description: Enables output drive on the control/data line.
+          Required when the interface is connected to a shared bus;
+          not needed for point-to-point connections.
+      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.
+          enable: 0 = disabled, 1 = enabled (QAIF_AIF_LANE_ENABLE).
+          direction: QAIF_AIF_LANE_DIR_TX (0) for TX (playback),
+                     QAIF_AIF_LANE_DIR_RX (1) for RX (capture).
+        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. Maps to hardware register
+          BITS_PER_LANE as (slot_width * bits_per_lane - 1).
+          For example, 2 slots with a 32-bit slot width gives
+          63 (= 32 * 2 - 1) in the register, representing 64
+          bits per lane per frame.
+    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:
+  - |
+    #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_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF1_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF2_IBIT_CLK>,
+                 <&audiocorecc AUDIO_CORE_CC_AIF_IF3_IBIT_CLK>;
+        clock-names = "lpass_config",
+                      "lpass_core_axim",
+                      "aud_dma",
+                      "aud_dma_mem",
+                      "bus",
+                      "aif_if0_ibit",
+                      "aif_if1_ibit",
+                      "aif_if2_ibit",
+                      "aif_if3_ibit";
+        #sound-dai-cells = <1>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        interrupts = <GIC_SPI 331 IRQ_TYPE_LEVEL_HIGH>;
+
+        qaif_aif_if2: aif-interface@2 {
+            compatible = "qcom,qaif-tdm-dai";
+            reg = <QAIF_MI2S_TDM_AIF2>;
+            qcom,qaif-aif-sync-mode = <1>;
+            qcom,qaif-aif-sync-src = <1>;
+            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;
+            qcom,qaif-aif-lane-config = <QAIF_AIF_LANE_ENABLE QAIF_AIF_LANE_DIR_RX>,
+                                        <QAIF_AIF_LANE_ENABLE QAIF_AIF_LANE_DIR_TX>;
+            qcom,qaif-aif-bits-per-lane = <2>;
+        };
+    };
diff --git a/include/dt-bindings/sound/qcom,qaif.h b/include/dt-bindings/sound/qcom,qaif.h
new file mode 100644
index 000000000000..0000b2f30379
--- /dev/null
+++ b/include/dt-bindings/sound/qcom,qaif.h
@@ -0,0 +1,89 @@
+/* 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	/* FRAME SYNC high for 1 INTF clock cycle */
+#define QAIF_AIF_SYNC_MODE_ONE_SLOT	1	/* FRAME SYNC high for 1 slot per frame */
+#define QAIF_AIF_SYNC_MODE_LONG		2	/* FRAME SYNC high for half the 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 v2 02/11] ASoC: qcom: Add QAIF hardware register map
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 10:57 ` [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 453 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 453 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..cb674bbb600a
--- /dev/null
+++ b/sound/soc/qcom/qaif-reg.h
@@ -0,0 +1,453 @@
+/* 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
+ */
+static inline u32 QAIF_RDDMA_REG_ADDR(const struct qaif_variant *v,
+				      u32 offset, u32 chan)
+{
+	return 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))
+
+static inline u32 QAIF_CODEC_RDDMA_REG_ADDR(const struct qaif_variant *v,
+					    u32 offset, u32 chan)
+{
+	return 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
+ */
+static inline u32 QAIF_WRDMA_REG_ADDR(const struct qaif_variant *v,
+				      u32 offset, u32 chan)
+{
+	return 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))
+
+static inline u32 QAIF_CODEC_WRDMA_REG_ADDR(const struct qaif_variant *v,
+					    u32 offset, u32 chan)
+{
+	return 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))
+
+static inline u32 QAIF_EE_RDDMA_IRQ_REG_ADDR(const struct qaif_variant *v,
+					     enum qaif_irq_type dma_type,
+					     u32 offset)
+{
+	if (dma_type == QAIF_AIF_IRQ)
+		return v->rddma_irq_reg_base + offset +
+		       v->rddma_irq_stride * v->ee;
+	return 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)
+
+static inline u32 QAIF_EE_WRDMA_IRQ_REG_ADDR(const struct qaif_variant *v,
+					     enum qaif_irq_type dma_type,
+					     u32 offset)
+{
+	if (dma_type == QAIF_AIF_IRQ)
+		return v->wrdma_irq_reg_base + offset +
+		       v->wrdma_irq_stride * v->ee;
+	return 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)
+
+static inline u32 QAIF_DMACFG_REG(const struct qaif_variant *v,
+				  u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_CFG_REG(v, chan) :
+			QAIF_CODEC_WRDMA_CFG_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_CFG_REG(v, chan) : QAIF_WRDMA_CFG_REG(v, chan);
+}
+
+static inline u32 QAIF_DMACTL_REG(const struct qaif_variant *v,
+				  u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_CTL_REG(v, chan) :
+			QAIF_CODEC_WRDMA_CTL_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_CTL_REG(v, chan) : QAIF_WRDMA_CTL_REG(v, chan);
+}
+
+static inline u32 QAIF_DMABUFF_REG(const struct qaif_variant *v,
+				   u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, chan) :
+			QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_BUFF_LEN_REG(v, chan) : QAIF_WRDMA_BUFF_LEN_REG(v, chan);
+}
+
+static inline u32 QAIF_DMACURR_REG(const struct qaif_variant *v,
+				   u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, chan) :
+			QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_CURR_ADDR_REG(v, chan) : QAIF_WRDMA_CURR_ADDR_REG(v, chan);
+}
+
+static inline u32 QAIF_DMAPER_REG(const struct qaif_variant *v,
+				  u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_PERIOD_CNT_REG(v, chan) :
+			QAIF_CODEC_WRDMA_PERIOD_CNT_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_PERIOD_CNT_REG(v, chan) : QAIF_WRDMA_PERIOD_CNT_REG(v, chan);
+}
+
+static inline u32 QAIF_DMAPER_LEN_REG(const struct qaif_variant *v,
+				      u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, chan) :
+			QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_PERIOD_LEN_REG(v, chan) : QAIF_WRDMA_PERIOD_LEN_REG(v, chan);
+}
+
+static inline u32 QAIF_DMABASE_REG(const struct qaif_variant *v,
+				   u32 chan, int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, chan) :
+			QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, chan);
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_BASE_ADDR_REG(v, chan) : QAIF_WRDMA_BASE_ADDR_REG(v, chan);
+}
+
+static inline u32 QAIF_SID_MAP_REG(int dir, unsigned int dai_id)
+{
+	if (is_cif_dma_port(dai_id))
+		return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+			QAIF_CODEC_RDDMA_SID_MAP_REG :
+			QAIF_CODEC_WRDMA_SID_MAP_REG;
+	return dir == SNDRV_PCM_STREAM_PLAYBACK ?
+		QAIF_RDDMA_SID_MAP_REG : QAIF_WRDMA_SID_MAP_REG;
+}
+
+#endif /* __QAIF_REG_H__ */
-- 
2.34.1


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

* [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
  2026-07-01 10:57 ` [PATCH v2 02/11] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:26   ` sashiko-bot
  2026-07-01 10:57 ` [PATCH v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 506 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 506 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..75d3a119d36b
--- /dev/null
+++ b/sound/soc/qcom/qaif.h
@@ -0,0 +1,506 @@
+/* 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_CLOCK_FREQ				38400000
+
+#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,
+	QAIF_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;
+
+	/* serializes qaif_init() and ref_cnt across concurrent opens */
+	struct mutex stream_lock;
+
+	/* 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_EXT_RDDMA_OVERLAP		= BIT(16),
+	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 qaif_dma_dir {
+	QAIF_DMA_RDDMA,
+	QAIF_DMA_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[QAIF_DMA_TYPE_MAX];
+	u32 wr_len;
+	u32 wrdma_shram_len;
+	u32 wrdma_shram_start_addr[QAIF_DMA_TYPE_MAX];
+
+	/* AIF RDDMA register fields */
+	const struct reg_field rddma_enable;
+	const struct reg_field rddma_reset;
+	const struct reg_field rddma_num_ot;
+	const struct reg_field rddma_dma_dyncclk;
+	const struct reg_field rddma_burst16;
+	const struct reg_field rddma_burst8;
+	const struct reg_field rddma_burst4;
+	const struct reg_field rddma_burst2;
+	const struct reg_field rddma_burst1;
+	const struct reg_field rddma_shram_wm;
+
+	/* AIF WRDMA register fields */
+	const struct reg_field wrdma_enable;
+	const struct reg_field wrdma_reset;
+	const struct reg_field wrdma_num_ot;
+	const struct reg_field wrdma_dma_dyncclk;
+	const struct reg_field wrdma_burst16;
+	const struct reg_field wrdma_burst8;
+	const struct reg_field wrdma_burst4;
+	const struct reg_field wrdma_burst2;
+	const struct reg_field wrdma_burst1;
+	const struct reg_field wrdma_shram_wm;
+
+	/* CODEC RDDMA register fields */
+	const struct reg_field cif_rddma_enable;
+	const struct reg_field cif_rddma_reset;
+	const struct reg_field cif_rddma_num_ot;
+	const struct reg_field cif_rddma_dma_dyncclk;
+	const struct reg_field cif_rddma_burst16;
+	const struct reg_field cif_rddma_burst8;
+	const struct reg_field cif_rddma_burst4;
+	const struct reg_field cif_rddma_burst2;
+	const struct reg_field cif_rddma_burst1;
+	const struct reg_field cif_rddma_shram_wm;
+	const struct reg_field cif_rddma_active_ch_en;
+	const struct reg_field cif_rddma_fs_sel;
+	const struct reg_field cif_rddma_fs_delay;
+	const struct reg_field cif_rddma_fs_out_gate;
+	const struct reg_field cif_rddma_intf_dyncclk;
+	const struct reg_field cif_rddma_en_16bit_unpack;
+
+	/* CODEC WRDMA register fields */
+	const struct reg_field cif_wrdma_enable;
+	const struct reg_field cif_wrdma_reset;
+	const struct reg_field cif_wrdma_num_ot;
+	const struct reg_field cif_wrdma_dma_dyncclk;
+	const struct reg_field cif_wrdma_burst16;
+	const struct reg_field cif_wrdma_burst8;
+	const struct reg_field cif_wrdma_burst4;
+	const struct reg_field cif_wrdma_burst2;
+	const struct reg_field cif_wrdma_burst1;
+	const struct reg_field cif_wrdma_shram_wm;
+	const struct reg_field cif_wrdma_active_ch_en;
+	const struct reg_field cif_wrdma_fs_sel;
+	const struct reg_field cif_wrdma_fs_delay;
+	const struct reg_field cif_wrdma_fs_out_gate;
+	const struct reg_field cif_wrdma_intf_dyncclk;
+	const struct reg_field cif_wrdma_en_16bit_unpack;
+
+	/* Regmap fields of AIF interface registers bitfields */
+	const struct reg_field aif_inv_sync;
+	const struct reg_field aif_sync_delay;
+	const struct reg_field aif_sync_mode;
+	const struct reg_field aif_sync_src;
+	const struct reg_field aif_sample_width_rx;
+	const struct reg_field aif_sample_width_tx;
+	const struct reg_field aif_slot_width_rx;
+	const struct reg_field aif_slot_width_tx;
+	const struct reg_field aif_bits_per_lane;
+	const struct reg_field aif_slot_en_tx_mask;
+	const struct reg_field aif_slot_en_rx_mask;
+	const struct reg_field aif_loopback_en;
+	const struct reg_field aif_ctrl_data_oe;
+	const struct reg_field aif_lane_en;
+	const struct reg_field aif_lane_dir;
+	const struct reg_field aif_mono_mode_rx;
+	const struct reg_field aif_mono_mode_tx;
+	const struct reg_field aif_full_cycle_en;
+
+	/* AUDIO_CORE_QAIF_AUD_INTFa_CTL bitfields */
+	const struct reg_field aif_enable;
+	const struct reg_field aif_enable_tx;
+	const struct reg_field aif_enable_rx;
+	const struct reg_field aif_reset;
+	const struct reg_field aif_reset_tx;
+	const 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 (*clk_init)(struct platform_device *pdev);
+	int (*clk_deinit)(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 v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (2 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:09   ` sashiko-bot
  2026-07-01 10:57 ` [PATCH v2 05/11] ASoC: qcom: Add QAIF AIF " Harendra Gautam
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 321 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 321 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..18df81e3ed9d
--- /dev/null
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -0,0 +1,321 @@
+// 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(*rd_dmactl), GFP_KERNEL);
+	if (!rd_dmactl)
+		return -ENOMEM;
+
+	wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL);
+	if (!wr_dmactl)
+		return -ENOMEM;
+
+	rd_intfctl = devm_kzalloc(dev, sizeof(*rd_intfctl), GFP_KERNEL);
+	if (!rd_intfctl)
+		return -ENOMEM;
+
+	wr_intfctl = devm_kzalloc(dev, sizeof(*wr_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 intf_dyncclk 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 codec_fs_sel 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 en_16bit_unpack 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 intf_dyncclk 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 en_16bit_unpack 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 v2 05/11] ASoC: qcom: Add QAIF AIF DAI ops
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (3 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:14   ` sashiko-bot
  2026-07-01 10:57 ` [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu Harendra Gautam
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 474 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 474 insertions(+)

diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
index 18df81e3ed9d..be524e64f806 100644
--- a/sound/soc/qcom/qaif-cpu.c
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -319,3 +319,477 @@ 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(*aif_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(*rd_dmactl), GFP_KERNEL);
+	if (!rd_dmactl)
+		return -ENOMEM;
+
+	wr_dmactl = devm_kzalloc(dev, sizeof(*wr_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 v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (4 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 05/11] ASoC: qcom: Add QAIF AIF " Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:11   ` sashiko-bot
  2026-07-02  7:12   ` Krzysztof Kozlowski
  2026-07-01 10:57 ` [PATCH v2 07/11] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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. Replace the
private implementation in lpass-cpu.c with a thin wrapper and add an
equivalent wrapper in qaif-cpu.c.

Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
 sound/soc/qcom/common.c    | 34 ++++++++++++++++++++++++++++++++++
 sound/soc/qcom/common.h    |  5 +++++
 sound/soc/qcom/lpass-cpu.c | 23 +++++++----------------
 sound/soc/qcom/qaif-cpu.c  | 16 ++++++++++++++++
 4 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index edc4611691f7..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 *dev = card->dev;
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
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 242bc16da36d..0c84964bafdc 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,
-				   const struct of_phandle_args *args,
-				   const char **dai_name)
+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,
 };
 
diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
index 08c7aa477938..93b75ea8e48f 100644
--- a/sound/soc/qcom/qaif-cpu.c
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -793,3 +793,19 @@ 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,
+};
-- 
2.34.1


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

* [PATCH v2 07/11] ASoC: qcom: Add QAIF regmap, DT parsing and platform init
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (5 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:11   ` sashiko-bot
  2026-07-02  7:07   ` Krzysztof Kozlowski
  2026-07-01 10:57 ` [PATCH v2 08/11] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
                   ` (4 subsequent siblings)
  11 siblings, 2 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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      | 799 +++++++++++++++++++++++++++++++++
 sound/soc/qcom/qaif-platform.c | 292 ++++++++++++
 2 files changed, 1091 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 93b75ea8e48f..371d7ef2f57b 100644
--- a/sound/soc/qcom/qaif-cpu.c
+++ b/sound/soc/qcom/qaif-cpu.c
@@ -809,3 +809,802 @@ 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 < QAIF_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 < QAIF_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 < QAIF_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");
+	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");
+	if (IS_ERR(data->aud_dma_mem_clk))
+		return PTR_ERR(data->aud_dma_mem_clk);
+
+	return 0;
+}
+
+/**
+ * asoc_qcom_qaif_cpu_platform_probe - Probe the QAIF CPU and platform driver
+ * @pdev: Platform device
+ *
+ * Initialises the QAIF regmap, parses DT, sets up clocks and registers
+ * the CPU DAI component and PCM platform.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+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;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), 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 = match->data;
+	variant = drvdata->variant;
+
+	ret = of_qaif_parse_aif_intf_cfg(dev, drvdata);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to parse aif interfaces\n");
+
+	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)
+		return dev_err_probe(dev, ret, "Failed to get cdc dma clocks\n");
+
+	if (variant->clk_init) {
+		ret = variant->clk_init(pdev);
+		if (ret)
+			return dev_err_probe(dev, ret, "Failed to initialize variant\n");
+		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])) {
+			ret = PTR_ERR(drvdata->mi2s_bit_clk[idx]);
+			dev_err_probe(dev, ret, "error getting %s\n",
+				      variant->dai_bit_clk_names[idx]);
+			goto err;
+		}
+	}
+
+	ret = qaif_aif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
+	if (ret) {
+		dev_err_probe(dev, ret, "error init aif bitfields\n");
+		goto err;
+	}
+
+	ret = qaif_aif_cfg_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
+	if (ret) {
+		dev_err_probe(dev, ret, "error init aif intfctl fields\n");
+		goto err;
+	}
+
+	ret = qaif_cif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
+	if (ret) {
+		dev_err_probe(dev, ret, "error init cif bitfields\n");
+		goto err;
+	}
+
+	ret = devm_snd_soc_register_component(dev,
+					      &qaif_cpu_comp_driver,
+					      variant->dai_driver,
+					      variant->num_dai);
+	if (ret) {
+		dev_err_probe(dev, ret, "error registering cpu driver\n");
+		goto err;
+	}
+
+	ret = asoc_qcom_qaif_platform_register(pdev);
+	if (ret) {
+		dev_err_probe(dev, ret, "error registering platform driver\n");
+		goto err;
+	}
+err:
+	if (ret && variant_init_done && variant->clk_deinit)
+		variant->clk_deinit(pdev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_probe);
+
+/**
+ * asoc_qcom_qaif_cpu_platform_remove - Remove the QAIF CPU and platform driver
+ * @pdev: Platform device
+ */
+void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+
+	if (drvdata->variant->clk_deinit)
+		drvdata->variant->clk_deinit(pdev);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_remove);
+
+/**
+ * asoc_qcom_qaif_cpu_platform_shutdown - Shutdown the QAIF CPU and platform driver
+ * @pdev: Platform device
+ */
+void asoc_qcom_qaif_cpu_platform_shutdown(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+
+	if (drvdata->variant->clk_deinit)
+		drvdata->variant->clk_deinit(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..1856389e643b
--- /dev/null
+++ b/sound/soc/qcom/qaif-platform.c
@@ -0,0 +1,292 @@
+// 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 v2 08/11] ASoC: qcom: Add QAIF PCM operations
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (6 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 07/11] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:12   ` sashiko-bot
  2026-07-01 10:57 ` [PATCH v2 09/11] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 626 +++++++++++++++++++++++++++++++++
 1 file changed, 626 insertions(+)

diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
index 12addfb85180..643def474aa0 100644
--- a/sound/soc/qcom/qaif-platform.c
+++ b/sound/soc/qcom/qaif-platform.c
@@ -290,2 +290,629 @@ static int qaif_init(struct snd_soc_component *component)
 	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 = -1, dir = substream->stream;
+	struct qaif_pcm_data *data;
+	struct qaif_dmactl *dmactl;
+	struct qaif_dma_mem_info *dma_mem_info = NULL;
+	struct regmap *map;
+	unsigned int dai_id = cpu_dai->driver->id;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	runtime->private_data = data;
+	map = drvdata->audio_qaif_map;
+
+	dmactl = qaif_get_dmactl_handle(substream, component);
+	if (!dmactl) {
+		ret = -EINVAL;
+		goto err_dealloc_mem;
+	}
+
+	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) {
+		ret = -ENOMEM;
+		goto err_dealloc_mem;
+	}
+
+	ret = clk_prepare_enable(drvdata->aud_dma_clk);
+	if (ret) {
+		dev_err(soc_runtime->dev, "failed to enable aud_dma_clk: %d\n", ret);
+		goto err_dealloc_mem;
+	}
+
+	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);
+		goto err_disable_dma_clk;
+	}
+
+	mutex_lock(&drvdata->stream_lock);
+	if (v->alloc_stream_dma_idx) {
+		stream_dma_idx = v->alloc_stream_dma_idx(drvdata, dir, dai_id);
+	} else {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
+
+	if (stream_dma_idx < 0) {
+		ret = stream_dma_idx;
+		goto err_unlock;
+	}
+
+	ret = qaif_init(component);
+	if (ret) {
+		dev_err(soc_runtime->dev, "qaif_init failed: %d\n", ret);
+		goto err_free_dma_idx;
+	}
+	drvdata->qaif_init_ref_cnt++;
+	mutex_unlock(&drvdata->stream_lock);
+
+	data->stream_dma_idx = stream_dma_idx;
+
+	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;
+		}
+		mutex_lock(&drvdata->stream_lock);
+		drvdata->qaif_init_ref_cnt--;
+		if (v->free_stream_dma_idx)
+			v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+		mutex_unlock(&drvdata->stream_lock);
+		goto err_disable_dma_mem_clk;
+	}
+
+	return 0;
+
+err_free_dma_idx:
+	if (v->free_stream_dma_idx)
+		v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+err_unlock:
+	mutex_unlock(&drvdata->stream_lock);
+err_disable_dma_mem_clk:
+	clk_disable_unprepare(drvdata->aud_dma_mem_clk);
+err_disable_dma_clk:
+	clk_disable_unprepare(drvdata->aud_dma_clk);
+err_dealloc_mem:
+	qaif_mem_dealloc_detach(component->dev, dma_mem_info);
+	kfree(data);
+	return ret;
+}
+
+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;
+	}
+
+	mutex_lock(&drvdata->stream_lock);
+	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);
+	mutex_unlock(&drvdata->stream_lock);
+
+	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, QAIF_DMA_CLOCK_FREQ);
+	clk_set_rate(drvdata->aud_dma_mem_clk, QAIF_DMA_CLOCK_FREQ);
+	dev_dbg(soc_runtime->dev,
+		"setting aud_dma_clk & aud_dma_mem_clk to %u\n",
+		QAIF_DMA_CLOCK_FREQ);
+
+	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 v2 09/11] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (7 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 08/11] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:27   ` sashiko-bot
  2026-07-01 10:57 ` [PATCH v2 10/11] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 331 +++++++++++++++++++++++++++++++++
 1 file changed, 331 insertions(+)

diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
index 643def474aa0..b2d175811b2f 100644
--- a/sound/soc/qcom/qaif-platform.c
+++ b/sound/soc/qcom/qaif-platform.c
@@ -916,2 +916,333 @@ 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 qaif_dma_dir 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 == QAIF_DMA_WRDMA)
+		stream_offset = (irq_type == QAIF_AIF_IRQ) ? v->wrdma_start
+							    : v->codec_wrdma_start;
+	else
+		stream_offset = 0;
+
+	if (dma_type == QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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 = drvdata->audio_qaif_map;
+
+	regcache_cache_only(map, true);
+	regcache_mark_dirty(map);
+	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;
+
+	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;
+}
+
+/**
+ * asoc_qcom_qaif_platform_register - Register the QAIF PCM platform component
+ * @pdev: Platform device
+ *
+ * Reads the SMMU stream ID from DT, requests the QAIF interrupt and
+ * registers the PCM platform component.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int asoc_qcom_qaif_platform_register(struct platform_device *pdev)
+{
+	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+	int ret;
+
+	if (!drvdata || !drvdata->variant)
+		return dev_err_probe(&pdev->dev, -EINVAL,
+				     "Invalid drvdata or variant\n");
+
+	ret = qaif_parse_smmu_sid(pdev, drvdata);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret,
+				     "Failed to parse SMMU SID\n");
+
+	drvdata->audio_qaif_irq = platform_get_irq(pdev, 0);
+	if (drvdata->audio_qaif_irq < 0)
+		return dev_err_probe(&pdev->dev, drvdata->audio_qaif_irq,
+				     "Failed to get IRQ\n");
+
+	ret = devm_request_irq(&pdev->dev, drvdata->audio_qaif_irq,
+			       asoc_platform_qaif_irq, IRQF_TRIGGER_HIGH,
+			       "qaif-irq-audio-core", drvdata);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "irq request failed\n");
+
+	drvdata->qaif_init_ref_cnt = 0;
+	mutex_init(&drvdata->stream_lock);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &qaif_component_driver,
+					      NULL, 0);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret,
+				     "Failed to register platform component\n");
+	return 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 v2 10/11] ASoC: qcom: Add Shikra QAIF support
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (8 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 09/11] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-01 11:22   ` sashiko-bot
  2026-07-02  7:01   ` Krzysztof Kozlowski
  2026-07-01 10:57 ` [PATCH v2 11/11] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
  2026-07-02  6:58 ` [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Krzysztof Kozlowski
  11 siblings, 2 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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 | 556 +++++++++++++++++++++++++++++++++++
 3 files changed, 569 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..de8803d5a044
--- /dev/null
+++ b/sound/soc/qcom/qaif-shikra.c
@@ -0,0 +1,556 @@
+// 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"
+
+static const 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 }
+};
+
+static const 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 }
+};
+
+static const 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 }
+};
+
+static const 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_and_set_bit(index, &drvdata->aif_dma_idx_bit_map))
+			return -EBUSY;
+		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_and_set_bit(index, &drvdata->cif_dma_idx_bit_map))
+			return -EBUSY;
+		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_clk_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->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)
+		return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable clocks\n");
+
+	return 0;
+}
+
+static int shikra_qaif_clk_deinit(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},
+
+	.rddma_enable	= REG_FIELD_ID(0x8000, 0, 0, 4, 0x1000),
+	.rddma_reset	= REG_FIELD_ID(0x8000, 4, 4, 4, 0x1000),
+
+	.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),
+
+	.wrdma_enable	= REG_FIELD_ID(0x11000, 0, 0, 4, 0x1000),
+	.wrdma_reset	= REG_FIELD_ID(0x11000, 4, 4, 4, 0x1000),
+
+	.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),
+
+	.cif_rddma_enable	= REG_FIELD_ID(0xC000, 0, 0, 4, 0x1000),
+	.cif_rddma_reset	= REG_FIELD_ID(0xC000, 4, 4, 4, 0x1000),
+
+	.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),
+
+	.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),
+
+	.cif_wrdma_enable	= REG_FIELD_ID(0x15000, 0, 0, 4, 0x1000),
+	.cif_wrdma_reset	= REG_FIELD_ID(0x15000, 4, 4, 4, 0x1000),
+
+	.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),
+
+	.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),
+
+	.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),
+
+	.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),
+
+	.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),
+
+	.aif_bits_per_lane	= REG_FIELD_ID(0x400C, 0, 9, 4, 0x1000),
+
+	.aif_slot_en_tx_mask	= REG_FIELD_ID(0x4010, 0, 31, 4, 0x1000),
+
+	.aif_slot_en_rx_mask	= REG_FIELD_ID(0x4030, 0, 31, 4, 0x1000),
+
+	.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),
+
+	.aif_mono_mode_rx	= REG_FIELD_ID(0x4054, 1, 1, 4, 0x1000),
+	.aif_mono_mode_tx	= REG_FIELD_ID(0x4054, 0, 0, 4, 0x1000),
+
+	.aif_full_cycle_en	= REG_FIELD_ID(0x4058, 0, 0, 4, 0x1000),
+
+	.clk_name			= (const char * const []) {
+						"lpass_config",
+						"lpass_core_axim",
+						"bus",
+					},
+	.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 * const []) {
+						"null",
+					},
+	.dai_bit_clk_names		= (const char * const []) {
+						"aif_if0_ibit",
+						"aif_if1_ibit",
+						"aif_if2_ibit",
+						"aif_if3_ibit",
+					},
+	.clk_init				= shikra_qaif_clk_init,
+	.clk_deinit				= shikra_qaif_clk_deinit,
+	.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 = 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

* [PATCH v2 11/11] MAINTAINERS: Add Qualcomm QAIF driver entry
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (9 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 10/11] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
@ 2026-07-01 10:57 ` Harendra Gautam
  2026-07-02  6:58 ` [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Krzysztof Kozlowski
  11 siblings, 0 replies; 30+ messages in thread
From: Harendra Gautam @ 2026-07-01 10:57 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-cpu.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

* Re: [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
@ 2026-07-01 11:04   ` Konrad Dybcio
  2026-07-02  6:52     ` Krzysztof Kozlowski
  2026-07-01 11:09   ` sashiko-bot
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 30+ messages in thread
From: Konrad Dybcio @ 2026-07-01 11:04 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 7/1/26 12:57 PM, 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.
> 
> 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.
> 
> Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller
>   support on Qualcomm Shikra SoC
>   https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/

This should most definitely not be part of the commit message (which
lands in git history..)

Konrad

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

* Re: [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
  2026-07-01 11:04   ` Konrad Dybcio
@ 2026-07-01 11:09   ` sashiko-bot
  2026-07-01 11:19   ` Mark Brown
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-01 11:09 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The property `qcom,qaif-aif-bits-per-lane` is fundamentally misnamed relative to its description and usage, creating a dangerous contradiction that will lead to incorrect hardware configurations.
- [Medium] The `qcom,qaif-aif-lane-config` matrix schema fails to enforce the exact length of each row, allowing malformed configurations to silently pass validation.
- [Low] The `compatible` property schema forbids standard specific-to-generic fallback chains.
--

commit 766077d779dbf4ceb1f7492bb1a02c81dfc5f0d3
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

dt-bindings: sound: qcom,qaif-cpu: Add binding

Add a dt-bindings header and devicetree binding for the Qualcomm Audio
Interface (QAIF) CPU DAI controller. This allows platform devicetree
files to describe the controller's MMIO region, IOMMU stream, clocks,
interrupts, and per-interface AIF configuration.

Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller support on Qualcomm Shikra SoC
  https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/

> diff --git a/Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml
> new file mode 100644
> index 0000000000000..672f40df877be
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,qaif-cpu.yaml
> @@ -0,0 +1,358 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/qcom,qaif-cpu.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#

[ ... ]

> +properties:
> +  compatible:
> +    enum:
> +      - qcom,qaif-cpu
> +      - qcom,shikra-qaif-cpu

[Severity: Low]
Does this strict enum restrict the compatible property to a single string,
preventing standard specific-to-generic fallback chains? If a device tree
attempts to use a fallback like "qcom,shikra-qaif-cpu", "qcom,qaif-cpu",
it looks like dt_binding_check validation would fail.

[ ... ]

> +      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.
> +          enable: 0 = disabled, 1 = enabled (QAIF_AIF_LANE_ENABLE).
> +          direction: QAIF_AIF_LANE_DIR_TX (0) for TX (playback),
> +                     QAIF_AIF_LANE_DIR_RX (1) for RX (capture).
> +        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]

[Severity: Medium]
Should this inner items list include minItems: 2 and maxItems: 2?

Since JSON Schema Draft 7 doesn't implicitly restrict array tuple lengths,
omitting the bounds allows extra elements per row to silently pass validation.

If malformed arrays are provided, it looks like the driver's flat array
parsing would misalign and misinterpret the values.

> +      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. Maps to hardware register
> +          BITS_PER_LANE as (slot_width * bits_per_lane - 1).
> +          For example, 2 slots with a 32-bit slot width gives
> +          63 (= 32 * 2 - 1) in the register, representing 64
> +          bits per lane per frame.

[Severity: High]
Is this property fundamentally misnamed? The description says it represents
the number of slots per lane, and the driver code also appears to treat
it as slots to calculate the final register value.

If a device tree author follows the property name qcom,qaif-aif-bits-per-lane
and provides the actual number of bits (like 64), wouldn't this cause a
register overflow and clock miscalculation when the driver multiplies
it by slot_width?

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

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

* Re: [PATCH v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops
  2026-07-01 10:57 ` [PATCH v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
@ 2026-07-01 11:09   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-01 11:09 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:
- [High] DAI callbacks fetch the DAI ID from the 0-th CPU DAI in the ALSA runtime link instead of using the provided `dai` parameter.
--

commit 32d00a3e9dcf1c7105fbcf435a7ab50e4e85ba7b
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

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

> diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
> new file mode 100644
> index 0000000000000..072c1bec078aa
> --- /dev/null
> +++ b/sound/soc/qcom/qaif-cpu.c
> @@ -0,0 +1,321 @@
[ ... ]
> +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;

[Severity: High]
Does this code fetch the incorrect DAI ID in multi-CPU DAI ALSA topologies?

By using snd_soc_rtd_to_cpu(soc_runtime, 0), the DAI ID is derived from the
hardcoded index 0 CPU DAI instead of the dai parameter passed to the function.

Could this cause mismatched data lookups and leave subsequent target DAIs
uninitialized?

[ ... ]
> +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;

[Severity: High]
Will deriving dai_id from the hardcoded index 0 CPU DAI configure the wrong
DMA channel for subsequent DAIs during hw_params() callbacks?

[ ... ]
> +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;

[Severity: High]
Can fetching dai_id via snd_soc_rtd_to_cpu(soc_runtime, 0) resolve the wrong
index during stream start/stop trigger events?

This could apply stream hardware configurations to the first CPU DAI
repeatedly instead of configuring the current dai, corrupting the DMA state
of the first DAI and leaving others uninitialized.

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

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

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

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Modifying a statically allocated, shared struct (`audio_qaif_regmap_config`) during probe causes race conditions and state corruption.
- [High] Clocks are manually deinitialized in remove() before devres unregisters the ASoC component, causing a devres ordering bug.
- [Medium] Unchecked underflow when passing `v->num_* - 1` to `GENMASK()`, leading to undefined behavior and hardware register corruption.
- [Medium] Bitwise ORing negative error codes returned by `regmap_write()` destroys original error values.
- [Medium] Unsigned variable `intf_idx` is checked for `< 0`, bypassing the intended error handler.
--

commit 70dc20df9813861f68ee157ba8ffd821195c8a29
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

ASoC: qcom: Add QAIF regmap, DT parsing and platform init

This commit adds the core CPU and platform bring-up needed for the Qualcomm
Audio Interface driver. It sets up the MMIO regmap, parses child nodes from
the devicetree for PCM/TDM/MI2S configurations, and initializes variant
resources and the ASoC CPU component.

> diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
> index 93b75ea8e48f5..99c3465466b5a 100644
> --- a/sound/soc/qcom/qaif-cpu.c
> +++ b/sound/soc/qcom/qaif-cpu.c

[ ... ]

> +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]
Can intf_idx ever be less than 0 here?

Since intf_idx is declared as a u32, this check will always evaluate to false.
If v->get_dma_idx() returns a negative error code, it will underflow into a
large positive number and bypass this intended error handling block.

> +				dev_err(dev, "Invalid DAI ID %d for interface '%s' (node %d)\n",
> +					dai_id, intf_np->name, num_interfaces);
> +				continue;
> +			}

[ ... ]

> +int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev)
> +{

[ ... ]

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

[Severity: High]
Does modifying audio_qaif_regmap_config.max_register directly cause issues if
multiple QAIF devices are probed?

Since audio_qaif_regmap_config is a statically allocated global structure,
multiple instances of the driver might race to overwrite this field, leaving
all instances with the register bounds of the last probed device.

> +	if (IS_ERR(drvdata->audio_qaif_map))
> +		return PTR_ERR(drvdata->audio_qaif_map);

[ ... ]

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

[Severity: High]
Does manually deinitializing the clocks here create a devres ordering issue?

The ASoC component is registered using devm_snd_soc_register_component(),
which means it will be unregistered during the devres teardown phase after
this remove function returns.

If the hardware clocks are already disabled here, will any teardown operations
or DAPM sequences that interact with the hardware registers cause a
synchronous external abort?

> diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
> new file mode 100644
> index 0000000000000..35c5a1e66c763
> --- /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: Medium]
Could v->num_rddma be zero for any hardware variants?

If a variant has 0 channels, v->num_rddma - 1 would underflow to U32_MAX,
causing GENMASK() to generate a fully populated mask. Writing this to the
hardware could map non-existent channels or corrupt reserved bits. There
doesn't appear to be a check for 0 here, unlike the v->num_intf check below.

> +	ret |= regmap_write(map, QAIF_EE_RDDMA_MAP_REG(v), mask);

[Severity: Medium]
Does bitwise ORing the return values from regmap_write() corrupt the error
codes?

Since regmap_write() returns negative errno values (like -EINVAL), bitwise
ORing two's complement negative numbers will mangle the return codes and
result in nonsensical error values being passed up the stack.

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

[ ... ]

> +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]
Similar to the issue above, does accumulating negative error codes using
bitwise OR here destroy the original error values returned by regmap_write()?

> +	ret |= regmap_write(map, QAIF_CODEC_RDDMA_MAP_QXM, qxm_sel);
> +	ret |= regmap_write(map, QAIF_CODEC_WRDMA_MAP_QXM, qxm_sel);

[ ... ]

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

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

* Re: [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu
  2026-07-01 10:57 ` [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu Harendra Gautam
@ 2026-07-01 11:11   ` sashiko-bot
  2026-07-02  7:12   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-01 11:11 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The LPASS CPU driver Kconfig definition does not select SND_SOC_QCOM_COMMON, leading to unresolved symbol linker errors.
--

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

ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu

This patch extracts common logic to resolve a sound-dai phandle argument
into a DAI name, placing it into common.c as asoc_qcom_of_xlate_dai_name().
It updates lpass-cpu and qaif-cpu to use this shared helper instead of their
private implementations.

> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index 242bc16da36da..0c84964bafdcd 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,
> -				   const struct of_phandle_args *args,
> -				   const char **dai_name)
> +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: High]
Since this uses a function exported from common.c, does SND_SOC_LPASS_CPU need
to select SND_SOC_QCOM_COMMON in Kconfig?

Without updating the Kconfig definition to select SND_SOC_QCOM_COMMON, this
can lead to unresolved symbol linker errors if a configuration enables an LPASS
machine driver but not the common components, or if SND_SOC_LPASS_CPU=y while
SND_SOC_QCOM_COMMON=m.

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

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

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

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] Setting ALSA DMA buffer type to SNDRV_DMA_TYPE_CONTINUOUS for dma_alloc_coherent() memory breaks mmap cache coherency.
- [High] qaif_platform_cdc_dma_mmap() uses the wrong device pointer, bypassing IOMMU translation.
- [High] The pointer callback returns negative error codes directly, corrupting the ALSA ring buffer state.
- [High] The close callback frees the DMA buffer and clears substream pointers without synchronizing against in-flight IRQ handlers.
- [Medium] Missing ALSA hardware step constraint for period and buffer bytes causes truncated DMA lengths.
- [Medium] The DMA engine is started before pending interrupts are cleared and enabled, and lacks error rollback.
- [Medium] Unhandled default case for unsupported dai_id in qaif_platform_pcmops_open() leads to leaked memory and uninitialized stream usage.
- [Low] Bitwise accumulation of negative error codes in IRQ configuration helpers corrupts the returned errno.
--

commit 58474411dd21afab7ec8d7878e3b69a5a008fbfb
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

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

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

[ ... ]

> @@ -291,3 +291,630 @@ 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;
>  
>  	dma_mem_info = qaif_mem_alloc_attach(component,

[Severity: High]
Does setting the buffer type to SNDRV_DMA_TYPE_CONTINUOUS for memory
allocated via dma_alloc_coherent() break mmap cache coherency in the
fallback snd_pcm_lib_default_mmap() handler?

[ ... ]

>  	switch (dai_id) {
>  	case QAIF_MI2S_TDM_AIF0 ... QAIF_MI2S_TDM_AIF12:
[ ... ]
>  	case QAIF_CDC_DMA_RX0 ... QAIF_CDC_DMA_RX9:
[ ... ]
>  		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);

[Severity: Medium]
Does this unhandled default case leak the allocated dma_mem_info and leave
the stream data uninitialized instead of returning an error for an
unsupported dai_id?

[ ... ]

>  static int qaif_platform_pcmops_close(struct snd_soc_component *component,
>  				      struct snd_pcm_substream *substream)
>  {
[ ... ]
>  	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;

[Severity: High]
Can this free the DMA buffer and clear the substream pointers while an
in-flight IRQ handler is still executing on another CPU, since there is no
synchronize_irq() or sync_stop callback used here?

[ ... ]

>  static int qaif_platform_pcmops_prepare(struct snd_soc_component *component,
>  					struct snd_pcm_substream *substream)
>  {
[ ... ]
>  	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: Medium]
Can this bit-shift truncate the DMA length if userspace requests a buffer
size that isn't a multiple of 8 bytes, since there is no
snd_pcm_hw_constraint_step() enforced in qaif_platform_pcmops_open()?

[ ... ]

>  static int qaif_platform_irq_enable(struct qaif_drv_data *drvdata,
>  				    int dir,
>  				    enum qaif_irq_type irq_type,
>  				    int idx)
>  {
>  	int ret = 0;
[ ... ]
>  	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);

[Severity: Low]
Does accumulating negative error codes with a bitwise OR corrupt the
returned errno?

[ ... ]

>  static int qaif_platform_pcmops_trigger(struct snd_soc_component *component,
>  					struct snd_pcm_substream *substream,
>  					int 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) {
[ ... ]
>  		}
>  		ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_ON);
>  		if (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);

[Severity: Medium]
Does enabling the DMA engine before clearing and enabling interrupts open a
race window where the first period interrupt can be lost? Also, if the IRQ
setup fails, should the error path roll back the DMA engine state?

[ ... ]

>  static snd_pcm_uframes_t qaif_platform_pcmops_pointer(struct snd_soc_component *component,
>  						      struct snd_pcm_substream *substream)
>  {
[ ... ]
>  	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;
>  	}

[Severity: High]
Can returning a negative error code from regmap_read() as an unsigned
snd_pcm_uframes_t corrupt the ALSA ring buffer state?

[ ... ]

>  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]
Should this use the component device that was used during allocation instead
of the card device to avoid bypassing IOMMU translation?

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

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

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

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Clock reference tracking bugs leading to unbalanced clk_disable and power management failure.
- [High] Silent failure on stream trigger (START) due to overwritten error code and missing hardware rollback.
- [High] The CPU DAI directly configures the Codec DAI with hardcoded slots, causing out-of-bounds array reads and violating ASoC component architecture.
- [High] Concurrent playback and capture streams on the same MI2S interface will overwrite and corrupt shared hardware registers and clock rates.
- [Low] Useless NULL check on an inline array element address.
--

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

This commit adds 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
over MI2S.

> diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c
> index 072c1bec078aa..08c7aa4779382 100644
> --- a/sound/soc/qcom/qaif-cpu.c
> +++ b/sound/soc/qcom/qaif-cpu.c
[ ... ]
> +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]);

[Severity: High]
If hw_params fails early, it might not enable the clock. Will the ALSA core
still call hw_free during stream cleanup? If so, does calling clk_disable()
on a clock that was never enabled cause a reference underflow and kernel
warning? 

Also, since the clock is enabled in both hw_params and trigger(START), but
only disabled in trigger(STOP) and hw_free, could this lead to an unbalanced
enable count that prevents the clock from ever powering down?

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

[Severity: High]
Is it possible for the CPU DAI to be part of a link with no codecs, such as
certain dummy or hostless links? If rtd->dai_link->num_codecs is 0, will
snd_soc_rtd_to_codec(rtd, 0) read past the end of the rtd->dais array?

> +	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;
[ ... ]
> +	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) {

[Severity: Low]
Does evaluating the address of the array element &drvdata->aif_intf_cfg[idx]
always result in a non-NULL pointer offset from the parent structure, making
this NULL check structurally impossible to fail?

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

[Severity: High]
Since the hardware supports concurrent, full-duplex playback and capture on the
same DAI physical index, what happens if one stream is actively playing while
the opposite stream is started? Does unconditionally overwriting shared
physical interface registers and resetting the clock rate corrupt the ongoing
audio stream?

> +	if (ret) {
> +		dev_err(dai->dev, "Failed to write inv_sync: %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);

[Severity: High]
Is it standard for a CPU DAI driver to directly configure the Codec DAI using
snd_soc_dai_set_tdm_slot and snd_soc_dai_set_sysclk? This appears to bypass
the machine driver, which is typically responsible for linking and configuring
both endpoints.

Additionally, since the return values of these configuration calls are ignored,
will the CPU DAI proceed incorrectly if the codec rejects these parameters?

> +
> +	return 0;
> +}
> +
> +static int qaif_aif_cpu_daiops_trigger(struct snd_pcm_substream *substream,
> +				       int cmd, struct snd_soc_dai *dai)
> +{
[ ... ]
> +	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]);

[Severity: High]
If the regmap_fields_write() call fails and logs an error to 'ret', does
execution continue to clk_enable()? If clk_enable() succeeds and returns 0,
will it overwrite the previous error code in 'ret', causing the function to
return success while the hardware interface is left disabled?

Also, if the regmap write succeeds but clk_enable() fails, should the hardware
state be rolled back to QAIF_AIF_CTL_ENABLE_OFF before returning the error?

> +		if (ret) {
> +			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
> +			return ret;
> +		}
> +		break;

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

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

* Re: [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
  2026-07-01 11:04   ` Konrad Dybcio
  2026-07-01 11:09   ` sashiko-bot
@ 2026-07-01 11:19   ` Mark Brown
  2026-07-01 12:26   ` Mark Brown
  2026-07-02  6:50   ` Krzysztof Kozlowski
  4 siblings, 0 replies; 30+ messages in thread
From: Mark Brown @ 2026-07-01 11:19 UTC (permalink / raw)
  To: Harendra Gautam
  Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-sound, linux-arm-msm,
	devicetree, linux-kernel

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

On Wed, Jul 01, 2026 at 04:27:47PM +0530, 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.

Please submit patches using subject lines reflecting the style for the
subsystem, this makes it easier for people to identify relevant patches.
Look at what existing commits in the area you're changing are doing and
make sure your subject lines visually resemble what they're doing.
There's no need to resubmit to fix this alone.

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

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

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

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing DAI descriptors for CDC DMA TX (capture), RX2-3, and VA_TX2-3 channels render hardware paths completely inaccessible.
- [Low] Monolithic module linking prevents addition of future SoC variants without build errors.
--

commit 8fb121461863147da2f4ca2b57d93d08f040ef59
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
Subject: ASoC: qcom: Add Shikra QAIF support

This commit adds the Shikra variant data for the Qualcomm Audio Interface
driver. It introduces register field definitions, DMA mappings, DAI descriptors,
and clock configuration. The new variant is hooked up to Kconfig and the qcom
ASoC Makefile.

> diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
> index 985ce2ae286ba..857bb2a032a2d 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

[ ... ]

> diff --git a/sound/soc/qcom/qaif-shikra.c b/sound/soc/qcom/qaif-shikra.c
> new file mode 100644
> index 0000000000000..6bf32d155f3a1
> --- /dev/null
> +++ b/sound/soc/qcom/qaif-shikra.c
> @@ -0,0 +1,556 @@

[ ... ]

> +static const 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 }
> +};
> +
> +static const 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,

[ ... ]

> +	}, {
> +		.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,
> +	},
> +};

[Severity: High]
The DMA mapping (shikra_cif_tx_dma_dai_map) specifies all 4 QAIF_CDC_DMA_TX
channels, but the shikra_qaif_cpu_dai_driver array seems to omit the WCD
capture (QAIF_CDC_DMA_TX) DAIs entirely. It also only provides 2 out of 4 for
the RX and VA_TX DAIs.

Will this cause sound card registration to fail when a machine driver
attempts to instantiate a PCM capture stream routing through
QAIF_CDC_DMA_TX0 (or TX1-3)?

[ ... ]

> +static struct platform_driver shikra_qaif_cpu_platform_driver = {
> +	.driver = {
> +		.name = "shikra-qaif-cpu",
> +		.of_match_table = 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);

[Severity: Low]
Because qaif-cpu.o, qaif-platform.o, and qaif-shikra.o are linked into a
single snd-soc-qcom-qaif.ko module, does using module_platform_driver()
here inadvertently claim the entire module's init/exit entry points?

Could this lead to build failures due to multiple init_module definitions
if a future SoC variant is added to snd-soc-qcom-qaif-y and also attempts
to register a platform driver?

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

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

* Re: [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface
  2026-07-01 10:57 ` [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
@ 2026-07-01 11:26   ` sashiko-bot
  0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-01 11:26 UTC (permalink / raw)
  To: Harendra Gautam; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The commit message claims to define QAIF-private MI2S port IDs for the senary and septenary interfaces, but they are completely missing from the code.
--

commit 8d9f33afe0d4dfdd253d758e8763fc39a3c80578
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

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

Add shared data structures, register field definitions, and variant
descriptors for the Qualcomm Audio Interface (QAIF). This introduces
the common headers needed by the CPU DAI and PCM platform drivers
for DMA channel allocation and interrupt handling.

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

[Severity: Medium]
Is this description accurate? Looking at the code added in qaif.h, it does
not appear to include any definitions for QAIF-private MI2S port IDs for
the senary and septenary interfaces. 

Did these definitions get left out of this patch, or should the commit
message be updated to reflect the current code?

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

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

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

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Incorrect Device Tree parsing for the SMMU Stream ID breaks probing on systems without an SMMU or with different #iommu-cells.
- [High] regcache_sync() in the component resume callback executes without enabling the required audio clocks, causing a bus fault.
- [High] snd_pcm_stop() is called from the IRQ handler without holding the required PCM stream lock.
- [High] Setting regmap to cache_only mode during component suspend without disabling the interrupt creates an unhandled IRQ storm.
- [Medium] The IRQ handler incorrectly returns IRQ_NONE after successfully clearing a hardware interrupt if the substream is NULL.
- [Medium] Undefined behavior from a negative bitshift when using GENMASK() on zero-count DMA channels.
- [Medium] The driver fails to notify the ALSA core when a hardware XRUN (overflow/underflow) occurs.
--

commit 28e9637822bfe9fbb6803c1d7c7e397c87d18f6e
Author: Harendra Gautam <harendra.gautam@oss.qualcomm.com>

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

This patch completes the QAIF PCM platform support by adding IRQ handling
for DMA events and platform registration. It also introduces suspend and
resume callbacks to handle register state while clocks are toggled, and
reads the SMMU stream ID from the device tree.

> diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
> index 85964a1760e50..d0738d85f5947 100644
> --- a/sound/soc/qcom/qaif-platform.c
> +++ b/sound/soc/qcom/qaif-platform.c
[ ... ]
> +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 qaif_dma_dir dma_type,
> +					enum qaif_irq irq,
> +					struct snd_pcm_substream **substream)
> +{
[ ... ]
> +	if (dma_type == QAIF_DMA_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);

[Severity: Medium]
Does this code cause undefined behavior if num_dma evaluates to 0?

Depending on the architecture and compiler, GENMASK(-1, 0) expands to a shift
by a negative number. This could create an invalid mask that accidentally
clears all interrupts or misconfigures the hardware for non-existent channels.

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

[Severity: Medium]
Does this miss notifying the ALSA core about the xrun condition?

The hardware interrupt is caught and logged, but it doesn't appear
snd_pcm_stop_xrun() is called. This might leave userspace applications
unaware of dropped audio frames or stream stalls, leading to broken
synchronization.

> +
> +			case QAIF_IRQ_ERROR:
> +				snd_pcm_stop(stream, SNDRV_PCM_STATE_DISCONNECTED);

[Severity: High]
Can this cause data races or waitqueue corruption?

Calling snd_pcm_stop() requires the caller to hold the PCM stream lock. Since
this is called directly from the IRQ handler in qaif_process_dma_irq()
without holding snd_pcm_stream_lock_irqsave(), it seems unsafe.

> +				dev_err(regmap_get_device(drvdata->audio_qaif_map),
> +					"QAIF bus error\n");
> +				ret = IRQ_HANDLED;
> +				break;
> +			}
> +		}
> +	}
> +	return ret;
> +}

[Severity: Medium]
Will this return IRQ_NONE after successfully clearing a hardware interrupt?

If a hardware interrupt fires and the handler successfully clears it via
the regmap_write() call earlier in qaif_process_dma_irq(), but the
corresponding substream is NULL (for example, due to a race during stream
teardown), ret remains IRQ_NONE.

If the generic IRQ subsystem registers this as unhandled multiple times,
could it permanently disable the IRQ line?

[ ... ]
> +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 = drvdata->audio_qaif_map;
> +
> +	regcache_cache_only(map, true);
> +	regcache_mark_dirty(map);
> +	return 0;
> +}

[Severity: High]
Could this lead to an interrupt storm during suspend?

If a system suspend is initiated, qaif_platform_pcmops_suspend() sets the
regmap to cache_only mode without calling disable_irq(). If an active
hardware IRQ fires before the kernel reaches the suspend_device_irqs() phase,
asoc_platform_qaif_irq() will attempt to read the volatile IRQSTAT register.

Since the regmap is in cache_only mode, it will return an error, causing
the handler to return IRQ_NONE without clearing the interrupt, leading to
an endless loop.

> +
> +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;
> +
> +	regcache_cache_only(map, false);
> +	return regcache_sync(map);
> +}

[Severity: High]
Does regcache_sync() safely execute here without clocks enabled?

It looks like regcache_sync() attempts to flush dirty register values
to the hardware over MMIO. If the audio clocks were disabled upon PCM close
and have not been re-enabled yet, this might cause a bus fault or
Data Abort during system resume.

[ ... ]
> +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 this incorrectly assume the format of the iommus property?

Manually reading index 1 of the iommus property bypasses the standard
of_parse_phandle_with_args() API. This could break probing on perfectly
valid hardware setups, such as systems without an SMMU or systems where
the SMMU has a different number of iommu-cells.

> +	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;
> +}
[ ... ]

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

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

* Re: [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
                     ` (2 preceding siblings ...)
  2026-07-01 11:19   ` Mark Brown
@ 2026-07-01 12:26   ` Mark Brown
  2026-07-02  6:50   ` Krzysztof Kozlowski
  4 siblings, 0 replies; 30+ messages in thread
From: Mark Brown @ 2026-07-01 12:26 UTC (permalink / raw)
  To: Harendra Gautam
  Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-sound, linux-arm-msm,
	devicetree, linux-kernel

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

On Wed, Jul 01, 2026 at 04:27:47PM +0530, 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.

> Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller
>   support on Qualcomm Shikra SoC
>   https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/

Is this an actual dependency of some kind or is this just something
that's randomly on the same board?  We do seem to be getting an awful
lot of serieses from Qualcomm with some random unclear stack of in
flight dependencies of various kinds which is not particularly helpful.

> Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
> +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

This just sounds like a generic serial port being programmed to
different formats, why do we need all these compatibles?

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

Details of Linux software interfaces should not be in the DT.

> +      qcom,qaif-aif-sync-mode:
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +        enum: [0, 1, 2]
> +        description:
> +          Defines the FRAME SYNC mode for the audio interface.
> +            0 = SHORT - FRAME SYNC is high for 1 INTF clock cycle per frame
> +            1 = ONE_SLOT - FRAME SYNC is high for 1 slot per frame (pulse
> +                stretched by MIN(RPCM_WIDTH, TPCM_WIDTH))
> +            2 = LONG - FRAME SYNC is high for half the frame duration

This is a detail of how the various interface formats are specified,
just specify interface formats in a standard manner.  Similar issues
apply to many other properties here, it really feels like this is just
inventing things from scratch rather than working like other audio
bindings and using standard properties.

> +          Sync clock/frame source. Independent of sync-mode;
> +          all combinations of sync-mode and sync-src are valid.
> +            0 = External source (slave)
> +            1 = Internal source (master)

Consumer and provider.

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

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

* Re: [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
                     ` (3 preceding siblings ...)
  2026-07-01 12:26   ` Mark Brown
@ 2026-07-02  6:50   ` Krzysztof Kozlowski
  4 siblings, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-02  6:50 UTC (permalink / raw)
  To: Harendra Gautam
  Cc: Srinivas Kandagatla, Mark Brown, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-sound, linux-arm-msm,
	devicetree, linux-kernel

On Wed, Jul 01, 2026 at 04:27:47PM +0530, 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.
> 
> 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.
> 
> Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller
>   support on Qualcomm Shikra SoC
>   https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/

There is no such tag. Why would you want to include it in the Git
history?

...

> +properties:
> +  compatible:
> +    enum:
> +      - qcom,qaif-cpu

How did this appear?

You ignored my previous comments (I checked one or two).


> +      - qcom,shikra-qaif-cpu
> +
> +  reg:
> +    maxItems: 1
> +
> +  iommus:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 9
> +
> +  clock-names:
> +    items:
> +      - const: lpass_config
> +      - const: lpass_core_axim
> +      - const: aud_dma
> +      - const: aud_dma_mem
> +      - const: bus
> +      - const: aif_if0_ibit
> +      - const: aif_if1_ibit
> +      - const: aif_if2_ibit
> +      - const: aif_if3_ibit
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  '#sound-dai-cells':
> +    const: 1
> +
> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 0
> +
> +

Why double line break?

> +patternProperties:
> +  "^aif-interface@[0-9a-f]+$":

Use consistent quotes.

> +    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
> +        enum: [0, 1, 2]
> +        description:
> +          Defines the FRAME SYNC mode for the audio interface.
> +            0 = SHORT - FRAME SYNC is high for 1 INTF clock cycle per frame
> +            1 = ONE_SLOT - FRAME SYNC is high for 1 slot per frame (pulse
> +                stretched by MIN(RPCM_WIDTH, TPCM_WIDTH))
> +            2 = LONG - FRAME SYNC is high for half the frame duration
> +      qcom,qaif-aif-sync-src:
> +        $ref: /schemas/types.yaml#/definitions/uint32
> +        enum: [0, 1]
> +        description:
> +          Sync clock/frame source. Independent of sync-mode;
> +          all combinations of sync-mode and sync-src are valid.
> +            0 = External source (slave)
> +            1 = Internal source (master)
> +      qcom,qaif-aif-invert-sync:
> +        type: boolean
> +        description: Invert the frame sync polarity.

There is no such style of such text flow. Open any existing binding.

Either you disregarded existing code or you just vibe coded something.
Both cases are not right.

Combining missed comments, that's a NAK

Best regards,
Krzysztof


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

* Re: [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding
  2026-07-01 11:04   ` Konrad Dybcio
@ 2026-07-02  6:52     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-02  6:52 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Harendra Gautam, Srinivas Kandagatla, Mark Brown, Liam Girdwood,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-sound,
	linux-arm-msm, devicetree, linux-kernel

On Wed, Jul 01, 2026 at 01:04:23PM +0200, Konrad Dybcio wrote:
> On 7/1/26 12:57 PM, 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.
> > 
> > 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.
> > 
> > Depends-on: [PATCH 0/4] clk: qcom: Add Audio Core clock controller
> >   support on Qualcomm Shikra SoC
> >   https://lore.kernel.org/linux-clk/20260605-shikra-audiocorecc-v1-0-7ee6b5f2d928@oss.qualcomm.com/
> 
> This should most definitely not be part of the commit message (which
> lands in git history..)

And internal review (by tools and/or by people) would tell that, so no
one ever reviewed this internally. Neither our internal tools did.

Best regards,
Krzysztof


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

* Re: [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform
  2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
                   ` (10 preceding siblings ...)
  2026-07-01 10:57 ` [PATCH v2 11/11] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
@ 2026-07-02  6:58 ` Krzysztof Kozlowski
  11 siblings, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-02  6:58 UTC (permalink / raw)
  To: Harendra Gautam
  Cc: Srinivas Kandagatla, Mark Brown, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-sound, linux-arm-msm,
	devicetree, linux-kernel

On Wed, Jul 01, 2026 at 04:27:46PM +0530, Harendra Gautam wrote:
> This series introduces the Qualcomm Audio Interface (QAIF) driver for
> the Shikra audio platform.

This wasn't ever reviewed internally (I checked).

Please do not send next version before you get:
1. Internal toolset checks
2. Basic internal review on internal mailing list

You repeat somehow standard mistakes which any reviewer would point out.
If your team cannot briefly review it internally to avoid simple
mistakes, then please tell them that it is a requirement and I will be
NAKing the patchset without reading it, till they do that brief/basic
review.

Best regards,
Krzysztof


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

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

On Wed, Jul 01, 2026 at 04:27:56PM +0530, Harendra Gautam wrote:
> +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_clk_init(struct platform_device *pdev)

I do not see any response to my comments, so my previous comments stay.

Address the comments and respond to them.

Best regards,
Krzysztof


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

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

On Wed, Jul 01, 2026 at 04:27:53PM +0530, Harendra Gautam wrote:
> +/**
> + * asoc_qcom_qaif_cpu_platform_probe - Probe the QAIF CPU and platform driver
> + * @pdev: Platform device
> + *
> + * Initialises the QAIF regmap, parses DT, sets up clocks and registers
> + * the CPU DAI component and PCM platform.
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +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;
> +
> +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), 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 = match->data;
> +	variant = drvdata->variant;
> +
> +	ret = of_qaif_parse_aif_intf_cfg(dev, drvdata);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to parse aif interfaces\n");
> +
> +	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);

Why are you modifiying static variable? So if second instance is probed,
how is this supposed to work?

> +	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)
> +		return dev_err_probe(dev, ret, "Failed to get cdc dma clocks\n");
> +
> +	if (variant->clk_init) {
> +		ret = variant->clk_init(pdev);
> +		if (ret)
> +			return dev_err_probe(dev, ret, "Failed to initialize variant\n");
> +		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])) {
> +			ret = PTR_ERR(drvdata->mi2s_bit_clk[idx]);

Wrong syntax. Please use dev_err_probe.


> +			dev_err_probe(dev, ret, "error getting %s\n",
> +				      variant->dai_bit_clk_names[idx]);
> +			goto err;
> +		}
> +	}
> +
> +	ret = qaif_aif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "error init aif bitfields\n");
> +		goto err;
> +	}
> +
> +	ret = qaif_aif_cfg_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "error init aif intfctl fields\n");
> +		goto err;
> +	}
> +
> +	ret = qaif_cif_cpu_init_bitfields(dev, drvdata->audio_qaif_map);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "error init cif bitfields\n");
> +		goto err;
> +	}

Why do you need to enable the clocks before all this init? Do you
actually access the device here (like its registers)?

> +
> +	ret = devm_snd_soc_register_component(dev,
> +					      &qaif_cpu_comp_driver,
> +					      variant->dai_driver,
> +					      variant->num_dai);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "error registering cpu driver\n");
> +		goto err;
> +	}
> +
> +	ret = asoc_qcom_qaif_platform_register(pdev);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "error registering platform driver\n");
> +		goto err;
> +	}
> +err:
> +	if (ret && variant_init_done && variant->clk_deinit)
> +		variant->clk_deinit(pdev);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_probe);
> +
> +/**
> + * asoc_qcom_qaif_cpu_platform_remove - Remove the QAIF CPU and platform driver
> + * @pdev: Platform device
> + */
> +void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
> +
> +	if (drvdata->variant->clk_deinit)
> +		drvdata->variant->clk_deinit(pdev);
> +}
> +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_remove);
> +
> +/**
> + * asoc_qcom_qaif_cpu_platform_shutdown - Shutdown the QAIF CPU and platform driver
> + * @pdev: Platform device
> + */
> +void asoc_qcom_qaif_cpu_platform_shutdown(struct platform_device *pdev)
> +{
> +	struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
> +
> +	if (drvdata->variant->clk_deinit)
> +		drvdata->variant->clk_deinit(pdev);
> +}
> +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_shutdown);

I do not understand why do you need wrappers over single call of single
driver. You do not have different variants. And even if you had
different variants, I claim they will have exactly the same clk init and
deinit.

Again, this looks awfully like sending us a dowsntream driver which is a
known antipattern of upstreaming.


Best regards,
Krzysztof


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

* Re: [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu
  2026-07-01 10:57 ` [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu Harendra Gautam
  2026-07-01 11:11   ` sashiko-bot
@ 2026-07-02  7:12   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 30+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-02  7:12 UTC (permalink / raw)
  To: Harendra Gautam
  Cc: Srinivas Kandagatla, Mark Brown, Liam Girdwood, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-sound, linux-arm-msm,
	devicetree, linux-kernel

On Wed, Jul 01, 2026 at 04:27:52PM +0530, Harendra Gautam wrote:
> 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.

Heh? Identically? So why even doing this?

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

I miss something - qaif_cpu_of_xlate_dai_name() wrapper and
lpass_cpu_of_xlate_dai_name() are identical. Why can't you use
asoc_qcom_of_xlate_dai_name() here directly (or
lpass_cpu_of_xlate_dai_name() without all this renaming)?



Best regards,
Krzysztof


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

end of thread, other threads:[~2026-07-02  7:12 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
2026-07-01 11:04   ` Konrad Dybcio
2026-07-02  6:52     ` Krzysztof Kozlowski
2026-07-01 11:09   ` sashiko-bot
2026-07-01 11:19   ` Mark Brown
2026-07-01 12:26   ` Mark Brown
2026-07-02  6:50   ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 02/11] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
2026-07-01 10:57 ` [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
2026-07-01 11:26   ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
2026-07-01 11:09   ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 05/11] ASoC: qcom: Add QAIF AIF " Harendra Gautam
2026-07-01 11:14   ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu Harendra Gautam
2026-07-01 11:11   ` sashiko-bot
2026-07-02  7:12   ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 07/11] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
2026-07-01 11:11   ` sashiko-bot
2026-07-02  7:07   ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 08/11] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
2026-07-01 11:12   ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 09/11] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
2026-07-01 11:27   ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 10/11] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
2026-07-01 11:22   ` sashiko-bot
2026-07-02  7:01   ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 11/11] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
2026-07-02  6:58 ` [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Krzysztof Kozlowski

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