From 4b832a2af523cbf857a977414c0f07d7f90a4c9d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Oct 2026 12:10:42 +0800 Subject: [PATCH 1/3] ASoC: qcom: q6apm: Fix unmap regions response handling The DSP acknowledges APM_CMD_SHARED_MEM_UNMAP_REGIONS with a GPR_BASIC_RSP_RESULT packet whose payload carries the acknowledged opcode and the status. apm_callback() stores hdr->opcode, i.e. GPR_BASIC_RSP_RESULT itself, as the result opcode, so audioreach_send_cmd_sync(), which waits for 0x0100100d, never sees a match and gives up after five seconds even though the DSP has already processed the command: qcom-apm gprsvc:service:2:1: CMD timeout for [100100d] opcode Each mapped graph costs a five second stall on unmap, and the forced status of 0 also hides real DSP failures from the caller. Record the opcode and status from the response payload, like the other basic-result commands do, and only drop the cached mapping handle when the DSP reports success. Fixes: 8ea6e25c8536 ("ASoC: qcom: q6apm: Add support for early buffer mapping on DSP") Cc: stable@vger.kernel.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20261021041044.2080369-2-shengchao.guo@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index b6b1ef6833b3..fa7f5dfca418 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -960,17 +960,16 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) wake_up(&apm->wait); break; case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - apm->result.opcode = hdr->opcode; - apm->result.status = 0; - rsp = data->payload; - - info = idr_find(&apm->graph_info_idr, hdr->token); - if (info) - info->mem_map_handle = 0; - else + apm->result.opcode = result->opcode; + apm->result.status = result->status; + if (result->status) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); + info = idr_find(&apm->graph_info_idr, hdr->token); + if (info && !result->status) + info->mem_map_handle = 0; + wake_up(&apm->wait); break; default: From 43c69604271d8b54c494fde70bd4a9eaab7a974d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Oct 2026 12:10:43 +0800 Subject: [PATCH 2/3] ASoC: qcom: q6apm: Clear the right handle on pos buffer unmap Push/pull graphs map two shared-memory regions per graph, the circular buffer and the position buffer, and keep a separate DSP handle for each. The map path tags its token with APM_MMAP_TOKEN_MAP_TYPE_POS_BUF so the response lands in the right handle, but the unmap path sends a bare graph ID and the response handler always clears mem_map_handle. Unmapping the position buffer therefore wipes the circular buffer's handle and leaves pos_buf_mem_map_handle set. A subsequent unmap of the circular buffer is skipped, leaking that DSP mapping, and a remap of the position buffer is skipped because the stale handle looks valid. Tag the unmap token the same way as the map token and decode it in the response handler to clear the matching handle. Fixes: 7c1ac23b178a ("ASoC: qcom: audioreach: Add support for shared memory push/pull modules") Cc: stable@vger.kernel.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20261021041044.2080369-3-shengchao.guo@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index fa7f5dfca418..0e5219a6513f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -329,8 +329,9 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; uint32_t mem_map_handle; + uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), - APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + APM_CMD_SHARED_MEM_UNMAP_REGIONS, (graph_id | pos_mask)); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -966,9 +967,16 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); - info = idr_find(&apm->graph_info_idr, hdr->token); - if (info && !result->status) - info->mem_map_handle = 0; + graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK; + is_pos_buf = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF; + + info = idr_find(&apm->graph_info_idr, graph_id); + if (info && !result->status) { + if (is_pos_buf) + info->pos_buf_mem_map_handle = 0; + else + info->mem_map_handle = 0; + } wake_up(&apm->wait); break; From a51c5dd7f7fc7cdf4ae28fec4a013d8418d1cb53 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Oct 2026 12:10:44 +0800 Subject: [PATCH 3/3] ASoC: qcom: q6apm: Unmap DSP buffers before removing the topology Since buffers are mapped once in pcm_new() and unmapped in pcm_free(), the unmap relies on the graph info still being registered. During card teardown, however, soc_cleanup_card_resources() removes the components before it frees the PCM runtimes. q6apm-audio's remove callback unloads the topology, which frees every audioreach_graph_info, so by the time q6apm_dai_pcm_free() runs the idr lookup fails and the unmap silently returns -ENODEV. The DSP keeps the old mapping. When the card is bound again, for example after unbinding and binding any codec on it, pcm_new() maps the same buffer once more, the DSP rejects it and the front-end PCM is not created: qcom-apm gprsvc:service:2:1: Error (1) Processing 0x0100100c cmd qcom-apm gprsvc:service:2:1: DSP returned error[100100c] 1 q6apm-dai ...: Audio Start: Buffer Allocation failed rc = -22 snd-sc8280xp sound: ASoC: can't create pcm MultiMedia1 Playback :-22 Release the circular and position buffer mappings of all graphs in q6apm_audio_remove() while the graph info still exists. The unmap helper now takes the q6apm instance directly, since it is called from the APM component rather than from a q6apm-dai child device. Fixes: 8ea6e25c8536 ("ASoC: qcom: q6apm: Add support for early buffer mapping on DSP") Cc: stable@vger.kernel.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20261021041044.2080369-4-shengchao.guo@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6apm.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 0e5219a6513f..bed3b64e8310 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -322,11 +322,10 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr } EXPORT_SYMBOL_GPL(q6apm_alloc_fragments); -static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id, +static int __q6apm_unmap_memory_fixed_region(struct q6apm *apm, unsigned int graph_id, bool is_pos_buf) { struct apm_cmd_shared_mem_unmap_regions *cmd; - struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; uint32_t mem_map_handle; uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; @@ -358,13 +357,15 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) { - return __q6apm_unmap_memory_fixed_region(dev, graph_id, false); + return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent), + graph_id, false); } EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region); int q6apm_unmap_pos_buffer(struct device *dev, unsigned int graph_id) { - return __q6apm_unmap_memory_fixed_region(dev, graph_id, true); + return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent), + graph_id, true); } EXPORT_SYMBOL_GPL(q6apm_unmap_pos_buffer); @@ -851,6 +852,21 @@ static int q6apm_audio_probe(struct snd_soc_component *component) static void q6apm_audio_remove(struct snd_soc_component *component) { + struct q6apm *apm = dev_get_drvdata(component->dev); + struct audioreach_graph_info *info; + int id; + + /* + * The card core removes components before freeing the PCMs, so + * q6apm-dai's pcm_free() runs after the topology (and graph_info) + * is gone and can no longer unmap. Release the DSP mappings here, + * otherwise the next pcm_new() fails to map the same buffer again. + */ + idr_for_each_entry(&apm->graph_info_idr, info, id) { + __q6apm_unmap_memory_fixed_region(apm, id, false); + __q6apm_unmap_memory_fixed_region(apm, id, true); + } + /* remove topology */ snd_soc_tplg_component_remove(component); }