* [PATCH v4 0/4] ASoC: qcom: enable audio on stage-2 protected DSPs (mDSP)
@ 2026-09-21 18:53 Ajay Kumar Nandam
2026-09-21 18:53 ` [PATCH v4 1/4] ASoC: qcom: q6apm: clear g_apm on driver removal Ajay Kumar Nandam
2026-09-21 18:53 ` [PATCH v4 2/4] ASoC: qcom: qdsp6: generalize GPR service domain Ajay Kumar Nandam
0 siblings, 2 replies; 3+ messages in thread
From: Ajay Kumar Nandam @ 2026-09-21 18:53 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Pierre-Louis Bossart, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: ajay.nandam, linux-sound, linux-arm-msm, linux-kernel,
devicetree, Pratyush Meduri, Mohit Sharma
On platforms such as Qualcomm Shikra, audio is served by the modem DSP
(mDSP) rather than the ADSP. The mDSP runs in a stage-2 protected context
and cannot use the SMMU, so the PCM buffers it consumes must live in
reserved-memory carveouts that are handed to the consumer VMIDs via a
hypervisor (SCM) memory assignment. This series adds that access model to
the q6apm DAI driver and its binding, alongside the existing stage-1/SMMU
(iommus) path, which is left untouched.
The driver detects the mDSP target at runtime from the GPR domain_id
(GPR_DOMAIN_ID_MODEM) and enables the SCM assignment path automatically.
VMIDs are hardcoded in the driver (HLOS + MSS_MSA + LPASS) rather than
read from DT, following the consensus from v2 review discussion with
Krzysztof Kozlowski and Srinivas Kandagatla.
Tested on Qualcomm Shikra with mDSP audio playback and capture.
Prior versions:
v1 (VMID binding + driver + GPR domain):
https://lore.kernel.org/all/20260609064038.492641-1-ajay.nandam@oss.qualcomm.com/
v1 (memory-region binding + DTS):
https://lore.kernel.org/all/20260618113509.2025881-1-ajay.nandam@oss.qualcomm.com/
v2:
https://lore.kernel.org/all/20260826-a2a-shikra-vmid-v5-v2-0-c3dc62354eee@oss.qualcomm.com/
v3:
https://lore.kernel.org/all/20260918-vmid-v3-v3-0-f1cbf47bf173@oss.qualcomm.com/
Changes since v3:
- Split DT binding changes into a separate patch (3/4) from the driver
implementation (4/4). (Rob Herring)
- SCM-assign the data-path pool (memory-region[1]) as a single whole-pool
operation at probe instead of per-stream in pcm_new()/compr_open().
- SCM-assign compressed audio stream buffers in compr_open() and
unassign in compr_free() when no data-path pool is present. v3 only
covered PCM streams.
- Fix error path in pcm_new: unassign SCM region if memory_map fails
after a successful SCM assign, preventing a resource leak.
- Account for PAGE_SIZE padding in reserved-memory pool budget
calculation. v3 under-reserved by PAGE_SIZE per stream, which could
overflow the pool at maximum concurrent stream count.
Changes since v2:
- Drop qcom,vmids DT property entirely; VMIDs are static per SoC and
hardcoded in the driver (HLOS, MSS_MSA, LPASS). This addresses
Krzysztof's concern that qcom,vmids is "not a hardware property at
all" and does not belong in DT. (Krzysztof Kozlowski)
- Drop oneOf / dependentRequired constraints from the binding since
qcom,vmids no longer exists. memory-region is simply optional.
- Detect mDSP from GPR domain_id (GPR_DOMAIN_ID_MODEM) instead of
keying off a DT property. No new compatible needed.
- Add QCOM_SCM_VMID_LPASS to the SCM destination list alongside
HLOS and MSS_MSA — required for the mDSP firmware to access
buffers.
- Switch reserved-memory path from snd_pcm_set_managed_buffer_all()
to snd_pcm_set_fixed_buffer_all() so the carveout is not subject
to the preallocate_dma module param. (Mark Brown)
- Use per-item descriptions for memory-region in the DT binding
instead of free-form text. (Krzysztof Kozlowski)
- Drop reserved-memory node from the binding example.
(Krzysztof Kozlowski)
- Register devm cleanup action before SCM assigns so probe-error
paths do not leak HLOS memory access.
- Reorder series: g_apm fix (1/4), GPR domain (2/4), DT binding
(3/4), SCM assign (4/4) for cleaner bisection.
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@oss.qualcomm.com>
---
Ajay Kumar Nandam (4):
ASoC: qcom: q6apm: clear g_apm on driver removal
ASoC: qcom: qdsp6: generalize GPR service domain
dt-bindings: sound: qcom,q6apm-dai: add memory-region and relax iommus
ASoC: qcom: q6apm-dai: add SCM buffer assignment for mDSP platforms
.../devicetree/bindings/sound/qcom,q6apm-dai.yaml | 12 +-
sound/soc/qcom/Kconfig | 1 +
sound/soc/qcom/qdsp6/audioreach.c | 12 +-
sound/soc/qcom/qdsp6/audioreach.h | 22 +-
sound/soc/qcom/qdsp6/q6apm-dai.c | 300 +++++++++++++++++++--
sound/soc/qcom/qdsp6/q6apm.c | 9 +-
sound/soc/qcom/qdsp6/q6apm.h | 2 +-
sound/soc/qcom/qdsp6/q6prm.c | 2 +
8 files changed, 326 insertions(+), 34 deletions(-)
---
base-commit: 3d5670d672ae08b8c534b7beed6f57c8b44e7b43
change-id: 20260921-vmid-v4-0116efe5937b
Best regards,
--
Ajay Kumar Nandam <ajay.nandam@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v4 1/4] ASoC: qcom: q6apm: clear g_apm on driver removal
2026-09-21 18:53 [PATCH v4 0/4] ASoC: qcom: enable audio on stage-2 protected DSPs (mDSP) Ajay Kumar Nandam
@ 2026-09-21 18:53 ` Ajay Kumar Nandam
2026-09-21 18:53 ` [PATCH v4 2/4] ASoC: qcom: qdsp6: generalize GPR service domain Ajay Kumar Nandam
1 sibling, 0 replies; 3+ messages in thread
From: Ajay Kumar Nandam @ 2026-09-21 18:53 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Pierre-Louis Bossart, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: ajay.nandam, linux-sound, linux-arm-msm, linux-kernel, devicetree
The global g_apm pointer is set during apm_probe() but never cleared
in apm_remove(). After the driver is removed the devm-managed struct
q6apm is freed, leaving g_apm dangling. A subsequent call to
q6apm_is_adsp_ready() dereferences the freed pointer.
Clear g_apm in apm_remove() before the component is unregistered so
that q6apm_is_adsp_ready() returns false instead of triggering a
use-after-free.
Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support")
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@oss.qualcomm.com>
---
sound/soc/qcom/qdsp6/q6apm.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 641d6d243229..12c6dfe4c58e 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -894,6 +894,7 @@ static int apm_probe(gpr_device_t *gdev)
static void apm_remove(gpr_device_t *gdev)
{
+ g_apm = NULL;
of_platform_depopulate(&gdev->dev);
snd_soc_unregister_component(&gdev->dev);
}
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v4 2/4] ASoC: qcom: qdsp6: generalize GPR service domain
2026-09-21 18:53 [PATCH v4 0/4] ASoC: qcom: enable audio on stage-2 protected DSPs (mDSP) Ajay Kumar Nandam
2026-09-21 18:53 ` [PATCH v4 1/4] ASoC: qcom: q6apm: clear g_apm on driver removal Ajay Kumar Nandam
@ 2026-09-21 18:53 ` Ajay Kumar Nandam
1 sibling, 0 replies; 3+ messages in thread
From: Ajay Kumar Nandam @ 2026-09-21 18:53 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Pierre-Louis Bossart, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: ajay.nandam, linux-sound, linux-arm-msm, linux-kernel,
devicetree, Pratyush Meduri
AudioReach builds APM and PRM command packets with the GPR destination
domain hardcoded to GPR_DOMAIN_ID_ADSP. This assumes audio is always
served by the ADSP, which is true for all currently supported targets.
On platforms such as Qualcomm Shikra, audio is served by the modem DSP
(mDSP) instead. The GPR node in DT already describes which DSP backs
the service via its qcom,domain property (e.g. GPR_DOMAIN_ID_MODEM),
and the GPR core exposes it as gdev->domain_id. But the AudioReach
packet builders ignore this and always target the ADSP, so every
APM/PRM command is routed to the wrong DSP on mDSP targets and audio
does not function.
Fix this by reading the GPR destination domain from gdev->domain_id
and stamping it in the send helpers (q6apm_send_cmd_sync,
audioreach_graph_send_cmd_sync, q6prm_send_cmd_sync) just before
dispatch. This centralizes the domain decision at the send layer
rather than threading it through every packet-allocation call site.
For the small number of async data-path sends that bypass the sync
helpers (write, read, compr, EOS), the domain is stamped inline
before gpr_send_port_pkt(). When no domain is available the helper
falls back to GPR_DOMAIN_ID_ADSP, so all existing ADSP targets
remain unchanged.
Co-developed-by: Pratyush Meduri <mpratyus@qti.qualcomm.com>
Signed-off-by: Pratyush Meduri <mpratyus@qti.qualcomm.com>
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@oss.qualcomm.com>
---
sound/soc/qcom/qdsp6/audioreach.c | 12 +++++++++---
sound/soc/qcom/qdsp6/audioreach.h | 22 +++++++++++++++-------
sound/soc/qcom/qdsp6/q6apm.c | 8 +++++++-
sound/soc/qcom/qdsp6/q6apm.h | 2 +-
sound/soc/qcom/qdsp6/q6prm.c | 2 ++
5 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index e6e9eb2e85aa..f7ae6d0db7e7 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -579,10 +579,10 @@ EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt);
int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock,
gpr_port_t *port, wait_queue_head_t *cmd_wait,
- const struct gpr_pkt *pkt, uint32_t rsp_opcode)
+ struct gpr_pkt *pkt, uint32_t rsp_opcode)
{
- const struct gpr_hdr *hdr = &pkt->hdr;
+ struct gpr_hdr *hdr = &pkt->hdr;
int rc;
mutex_lock(cmd_lock);
@@ -622,10 +622,12 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
}
EXPORT_SYMBOL_GPL(audioreach_send_cmd_sync);
-int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt,
+int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
uint32_t rsp_opcode)
{
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
+
return audioreach_send_cmd_sync(graph->dev, NULL, &graph->result, &graph->lock,
graph->port, &graph->cmd_wait, pkt, rsp_opcode);
}
@@ -970,6 +972,8 @@ int audioreach_compr_set_param(struct q6apm_graph *graph,
if (rc)
return rc;
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
+
return gpr_send_port_pkt(graph->port, pkt);
}
EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
@@ -1489,6 +1493,8 @@ int audioreach_shared_memory_send_eos(struct q6apm_graph *graph)
eos->policy = WR_SH_MEM_EP_EOS_POLICY_LAST;
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
+
return gpr_send_port_pkt(graph->port, pkt);
}
EXPORT_SYMBOL_GPL(audioreach_shared_memory_send_eos);
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 62a2fd79bbcb..2ae7b402a137 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -912,14 +912,19 @@ struct audioreach_module_config {
};
/* Packet Allocation routines */
-void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t
- token);
+static inline u16 audioreach_gpr_dest_domain(gpr_device_t *gdev)
+{
+ return gdev && gdev->domain_id ? gdev->domain_id : GPR_DOMAIN_ID_ADSP;
+}
+
+void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode,
+ uint32_t token);
void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels);
void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode,
uint32_t token, uint32_t src_port,
uint32_t dest_port);
void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
- uint32_t src_port);
+ uint32_t src_port);
void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
uint32_t token, uint32_t src_port,
uint32_t dest_port);
@@ -930,10 +935,13 @@ int audioreach_tplg_init(struct snd_soc_component *component);
/* Module specific */
void audioreach_graph_free_buf(struct q6apm_graph *graph);
-int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result,
- struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait,
- const struct gpr_pkt *pkt, uint32_t rsp_opcode);
-int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt,
+int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
+ struct gpr_ibasic_rsp_result_t *result,
+ struct mutex *cmd_lock, gpr_port_t *port,
+ wait_queue_head_t *cmd_wait,
+ struct gpr_pkt *pkt, uint32_t rsp_opcode);
+int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph,
+ struct gpr_pkt *pkt,
uint32_t rsp_opcode);
int audioreach_set_media_format(struct q6apm_graph *graph,
const struct audioreach_module *module,
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 12c6dfe4c58e..1845eb7b5739 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -29,11 +29,13 @@ struct apm_graph_mgmt_cmd {
static struct q6apm *g_apm;
-int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt,
+int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
uint32_t rsp_opcode)
{
gpr_device_t *gdev = apm->gdev;
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(gdev);
+
return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
NULL, &apm->wait, pkt, rsp_opcode);
}
@@ -502,6 +504,8 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
mutex_unlock(&graph->lock);
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
+
return gpr_send_port_pkt(graph->port, pkt);
}
EXPORT_SYMBOL_GPL(q6apm_write_async);
@@ -536,6 +540,8 @@ int q6apm_read(struct q6apm_graph *graph)
mutex_unlock(&graph->lock);
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
+
return gpr_send_port_pkt(graph->port, pkt);
}
EXPORT_SYMBOL_GPL(q6apm_read);
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
index 5cb51ca491dc..9092359ccf90 100644
--- a/sound/soc/qcom/qdsp6/q6apm.h
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -147,7 +147,7 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph,
int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir);
int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id);
/* Helpers */
-int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt,
+int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
uint32_t rsp_opcode);
/* Callback for graph specific */
diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c
index 04892fb4423f..f93383078eb1 100644
--- a/sound/soc/qcom/qdsp6/q6prm.c
+++ b/sound/soc/qcom/qdsp6/q6prm.c
@@ -51,6 +51,8 @@ struct prm_cmd_release_rsc {
static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
{
+ pkt->hdr.dest_domain = audioreach_gpr_dest_domain(prm->gdev);
+
return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
NULL, &prm->wait, pkt, rsp_opcode);
}
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-21 18:54 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21 18:53 [PATCH v4 0/4] ASoC: qcom: enable audio on stage-2 protected DSPs (mDSP) Ajay Kumar Nandam
2026-09-21 18:53 ` [PATCH v4 1/4] ASoC: qcom: q6apm: clear g_apm on driver removal Ajay Kumar Nandam
2026-09-21 18:53 ` [PATCH v4 2/4] ASoC: qcom: qdsp6: generalize GPR service domain Ajay Kumar Nandam
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®