From 4f52c08711cca2a41e8c26a68f997d5342b7b1ea Mon Sep 17 00:00:00 2001 From: Pratyush Meduri Date: Wed, 17 Jun 2026 22:42:06 +0530 Subject: [PATCH 1/2] ASoC: qcom: q6apm: Propagate shared memory unmap status Use the result payload returned for APM_CMD_SHARED_MEM_UNMAP_REGIONS instead of forcing a successful completion. Clear the memory map handle only when the DSP reports success, and keep error reporting tied to the returned status so callers can detect failed unmap operations. Signed-off-by: Pratyush Meduri --- sound/soc/qcom/qdsp6/q6apm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 355230e3f841..c795a76cc140 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -851,14 +851,13 @@ 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; + apm->result.opcode = result->opcode; + apm->result.status = result->status; info = idr_find(&apm->graph_info_idr, hdr->token); - if (info) + if (info && !result->status) info->mem_map_handle = 0; - else + else if (!info || result->status) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); From e233b1bb59b8e4e20deca02cdcd5968084ce84a9 Mon Sep 17 00:00:00 2001 From: Pratyush Meduri Date: Wed, 17 Jun 2026 22:42:06 +0530 Subject: [PATCH 2/2] ASoC: qcom: q6apm-dai: Support reserved memory buffers Add support for q6apm DAI instances that allocate PCM buffers from a reserved memory carveout instead of an IOMMU-mapped DMA allocation. Parse the optional memory-region property, size the ALSA buffer constraints to the carveout, and map the fixed physical region to the DSP graph when the stream is prepared. Support TrustZone VMID reassignment for both normal DMA buffers and reserved memory. Allow platforms to specify the source VMID tracked by SCM, share the buffer with the configured destination VMIDs, and restore ownership during stream teardown. Signed-off-by: Pratyush Meduri --- sound/soc/qcom/qdsp6/q6apm-dai.c | 658 +++++++++++++++++++++++++++---- 1 file changed, 589 insertions(+), 69 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 5d976438961d..5150010d3eb7 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -30,12 +32,15 @@ #define CAPTURE_MIN_PERIOD_SIZE 6144 #define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE) #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE) +#define Q6APM_RESERVED_BUFFER_BYTES_MAX (256 * 1024) +#define Q6APM_STATIC_AUDIO_CARVEOUT_ADDR 0x86200000ULL +#define Q6APM_STATIC_AUDIO_CARVEOUT_SIZE 0x40000 #define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) #define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) #define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) #define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) #define Q6APM_MAX_VMIDS 8 -#define Q6APM_SCM_MAX_VMID 31 +#define Q6APM_SCM_MAX_VMID QCOM_SCM_VMID_ADSP_HEAP #define SID_MASK_DEFAULT 0xF static const struct snd_compr_codec_caps q6apm_compr_caps = { @@ -79,6 +84,7 @@ struct q6apm_dai_rtd { snd_pcm_uframes_t queue_ptr; bool next_track; bool scm_assigned; + bool memory_mapped; u64 scm_src_perms; enum stream_state state; struct q6apm_graph *graph; @@ -87,26 +93,48 @@ struct q6apm_dai_rtd { }; struct q6apm_dai_data { + struct device *dev; long long sid; int num_vmids; u32 vmids[Q6APM_MAX_VMIDS]; + u32 src_vmid; bool use_scm_assign; + bool use_reserved_mem; + bool reserved_mem_assigned; + phys_addr_t reserved_mem_addr; + size_t reserved_mem_size; + bool aux_mem_assigned; + phys_addr_t aux_mem_addr; + size_t aux_mem_size; + size_t buffer_bytes_max; + u64 reserved_mem_src_perms; + u64 aux_mem_src_perms; }; static int q6apm_dai_assign_memory(struct q6apm_dai_rtd *prtd, const struct q6apm_dai_data *pdata) { struct qcom_scm_vmperm *dst_vmids; + phys_addr_t aligned_addr; + size_t aligned_size; + phys_addr_t addr; + size_t offset; int dst_count = 0; int ret; int i; + struct device *dev = prtd->substream->pcm->card->dev; - if (!pdata->use_scm_assign || pdata->num_vmids <= 0 || prtd->scm_assigned) + if (!pdata->use_scm_assign || pdata->use_reserved_mem || + pdata->num_vmids <= 0 || prtd->scm_assigned) return 0; /* hw_params() validates and sets dma_addr_valid before prepare(). */ - if (!prtd->dma_addr_valid || !prtd->pcm_size) + if (!prtd->dma_addr_valid || !prtd->pcm_size) { + dev_err(dev, + "SCM assign: buffer not ready (dma_addr_valid=%d pcm_size=%u)\n", + prtd->dma_addr_valid, prtd->pcm_size); return -EINVAL; + } dst_vmids = kcalloc(pdata->num_vmids + 1, sizeof(*dst_vmids), GFP_KERNEL); if (!dst_vmids) @@ -118,11 +146,8 @@ static int q6apm_dai_assign_memory(struct q6apm_dai_rtd *prtd, dst_count++; for (i = 0; i < pdata->num_vmids; i++) { - /* - * Probe-time validation rejects HLOS in qcom,vmid, so this is - * only a defensive check for future non-DT vmids[] population. - */ - if (WARN_ON_ONCE(pdata->vmids[i] == QCOM_SCM_VMID_HLOS)) + /* HLOS is already added above; skip it if listed in qcom,vmid. */ + if (pdata->vmids[i] == QCOM_SCM_VMID_HLOS) continue; dst_vmids[dst_count].vmid = pdata->vmids[i]; @@ -136,15 +161,34 @@ static int q6apm_dai_assign_memory(struct q6apm_dai_rtd *prtd, return 0; } - prtd->scm_size = ALIGN(prtd->pcm_size, PAGE_SIZE); - prtd->scm_src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + addr = prtd->dma_addr; + aligned_addr = ALIGN_DOWN(addr, PAGE_SIZE); + offset = addr - aligned_addr; + aligned_size = PAGE_ALIGN(prtd->pcm_size + offset); + + prtd->scm_size = aligned_size; + prtd->scm_src_perms = BIT_ULL(pdata->src_vmid); + + dev_dbg(dev, + "SCM assign: addr=%pa size=%zu src_vmid=0x%x dst_count=%d\n", + &aligned_addr, prtd->scm_size, pdata->src_vmid, dst_count); + for (i = 0; i < dst_count; i++) + dev_dbg(dev, "SCM assign: dst[%d] vmid=0x%x perm=0x%x\n", + i, dst_vmids[i].vmid, dst_vmids[i].perm); - ret = qcom_scm_assign_mem(prtd->dma_addr, prtd->scm_size, + ret = qcom_scm_assign_mem(aligned_addr, prtd->scm_size, &prtd->scm_src_perms, dst_vmids, dst_count); kfree(dst_vmids); - if (ret) + if (ret) { + dev_err(dev, + "SCM assign failed: addr=%pa size=%zu src_vmid=0x%x ret=%d\n", + &aligned_addr, prtd->scm_size, pdata->src_vmid, ret); return ret; + } + dev_dbg(dev, + "SCM assign ok: addr=%pa size=%zu src_perms_after=0x%llx\n", + &aligned_addr, prtd->scm_size, prtd->scm_src_perms); prtd->scm_assigned = true; return 0; } @@ -157,24 +201,254 @@ static int q6apm_dai_unassign_memory(struct q6apm_dai_rtd *prtd, .perm = QCOM_SCM_PERM_RW, }; struct device *dev = prtd->substream->pcm->card->dev; + phys_addr_t aligned_addr; + size_t aligned_size; + phys_addr_t addr; + size_t offset; int ret; if (!pdata->use_scm_assign || !prtd->scm_assigned) return 0; - ret = qcom_scm_assign_mem(prtd->dma_addr, prtd->scm_size, + addr = prtd->dma_addr; + aligned_addr = ALIGN_DOWN(addr, PAGE_SIZE); + offset = addr - aligned_addr; + aligned_size = PAGE_ALIGN(prtd->pcm_size + offset); + + dev_dbg(dev, + "SCM unassign: addr=%pa size=%zu src_perms=0x%llx\n", + &aligned_addr, aligned_size, prtd->scm_src_perms); + + ret = qcom_scm_assign_mem(aligned_addr, aligned_size, &prtd->scm_src_perms, &hlos, 1); if (!ret) { prtd->scm_assigned = false; prtd->scm_src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + dev_dbg(dev, "SCM unassign ok: addr=%pa\n", &aligned_addr); } else { dev_err(dev, "Failed to unassign DMA buffer %pa from VMIDs: %d\n", &prtd->dma_addr, ret); + dev_err(dev, + "SCM unassign failed: addr=%pa size=%zu src_perms=0x%llx ret=%d\n", + &aligned_addr, aligned_size, prtd->scm_src_perms, ret); + } + + return ret; +} + +static int q6apm_dai_assign_reserved_memory(struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm *dst_vmids; + struct device *dev = pdata->dev; + int dst_count = 0; + int ret; + int i; + + if (!pdata->use_reserved_mem || !pdata->use_scm_assign || + pdata->reserved_mem_assigned) + return 0; + + dst_vmids = kcalloc(pdata->num_vmids + 1, sizeof(*dst_vmids), GFP_KERNEL); + if (!dst_vmids) + return -ENOMEM; + + dst_vmids[dst_count].vmid = QCOM_SCM_VMID_HLOS; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + + for (i = 0; i < pdata->num_vmids; i++) { + if (pdata->vmids[i] == QCOM_SCM_VMID_HLOS) + continue; + + dst_vmids[dst_count].vmid = pdata->vmids[i]; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + } + if (dst_count == 1) { + kfree(dst_vmids); + return 0; + } + + pdata->reserved_mem_src_perms = BIT_ULL(pdata->src_vmid); + + dev_dbg(dev, + "SCM reserved assign begin: addr=%pa size=%zu src_vmid=0x%x dst_count=%d already_assigned=%d\n", + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + pdata->src_vmid, dst_count, pdata->reserved_mem_assigned); + for (i = 0; i < dst_count; i++) + dev_dbg(dev, "SCM reserved assign dst[%d]: vmid=0x%x perm=0x%x\n", + i, dst_vmids[i].vmid, dst_vmids[i].perm); + + ret = qcom_scm_assign_mem(pdata->reserved_mem_addr, + pdata->reserved_mem_size, + &pdata->reserved_mem_src_perms, + dst_vmids, dst_count); + if (ret) { + dev_err(dev, + "SCM reserved assign failed: addr=%pa size=%zu src_vmid=0x%x ret=%d\n", + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + pdata->src_vmid, ret); + } else { + pdata->reserved_mem_assigned = true; + dev_dbg(dev, + "SCM reserved assign ok: addr=%pa size=%zu src_perms_after=0x%llx\n", + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + pdata->reserved_mem_src_perms); + } + + kfree(dst_vmids); + return ret; +} + +static int q6apm_dai_assign_aux_memory(struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm *dst_vmids; + struct device *dev = pdata->dev; + int dst_count = 0; + int ret; + int i; + + if (!pdata->use_scm_assign || pdata->aux_mem_assigned || + !pdata->aux_mem_addr || !pdata->aux_mem_size) + return 0; + + dst_vmids = kcalloc(pdata->num_vmids + 1, sizeof(*dst_vmids), GFP_KERNEL); + if (!dst_vmids) + return -ENOMEM; + + dst_vmids[dst_count].vmid = QCOM_SCM_VMID_HLOS; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + + for (i = 0; i < pdata->num_vmids; i++) { + if (pdata->vmids[i] == QCOM_SCM_VMID_HLOS) + continue; + + dst_vmids[dst_count].vmid = pdata->vmids[i]; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + } + if (dst_count == 1) { + kfree(dst_vmids); + return 0; + } + + pdata->aux_mem_src_perms = BIT_ULL(pdata->src_vmid); + + dev_dbg(dev, + "SCM aux assign begin: addr=%pa size=%zu src_vmid=0x%x dst_count=%d already_assigned=%d\n", + &pdata->aux_mem_addr, pdata->aux_mem_size, + pdata->src_vmid, dst_count, pdata->aux_mem_assigned); + for (i = 0; i < dst_count; i++) + dev_dbg(dev, "SCM aux assign dst[%d]: vmid=0x%x perm=0x%x\n", + i, dst_vmids[i].vmid, dst_vmids[i].perm); + + ret = qcom_scm_assign_mem(pdata->aux_mem_addr, pdata->aux_mem_size, + &pdata->aux_mem_src_perms, dst_vmids, dst_count); + if (ret) { + dev_err(dev, + "SCM aux assign failed: addr=%pa size=%zu src_vmid=0x%x ret=%d\n", + &pdata->aux_mem_addr, pdata->aux_mem_size, + pdata->src_vmid, ret); + } else { + pdata->aux_mem_assigned = true; + dev_dbg(dev, + "SCM aux assign ok: addr=%pa size=%zu src_perms_after=0x%llx\n", + &pdata->aux_mem_addr, pdata->aux_mem_size, + pdata->aux_mem_src_perms); } + kfree(dst_vmids); return ret; } +static int q6apm_dai_unassign_reserved_memory(struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm hlos = { + .vmid = QCOM_SCM_VMID_HLOS, + .perm = QCOM_SCM_PERM_RW, + }; + struct device *dev = pdata->dev; + int ret; + + if (!pdata->use_reserved_mem || !pdata->reserved_mem_assigned) { + dev_dbg(dev, + "SCM reserved unassign skipped: use_reserved=%d assigned=%d\n", + pdata->use_reserved_mem, pdata->reserved_mem_assigned); + return 0; + } + + dev_dbg(dev, + "SCM reserved unassign begin: addr=%pa size=%zu src_perms=0x%llx\n", + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + pdata->reserved_mem_src_perms); + + ret = qcom_scm_assign_mem(pdata->reserved_mem_addr, + pdata->reserved_mem_size, + &pdata->reserved_mem_src_perms, &hlos, 1); + if (ret) { + dev_err(dev, + "SCM reserved unassign failed: addr=%pa size=%zu src_perms=0x%llx ret=%d\n", + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + pdata->reserved_mem_src_perms, ret); + return ret; + } + + pdata->reserved_mem_assigned = false; + pdata->reserved_mem_src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + dev_dbg(dev, "SCM reserved unassign ok: addr=%pa\n", + &pdata->reserved_mem_addr); + + return 0; +} + +static int q6apm_dai_unassign_aux_memory(struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm hlos = { + .vmid = QCOM_SCM_VMID_HLOS, + .perm = QCOM_SCM_PERM_RW, + }; + struct device *dev = pdata->dev; + int ret; + + if (!pdata->aux_mem_assigned) { + dev_dbg(dev, "SCM aux unassign skipped: assigned=%d\n", + pdata->aux_mem_assigned); + return 0; + } + + dev_dbg(dev, + "SCM aux unassign begin: addr=%pa size=%zu src_perms=0x%llx\n", + &pdata->aux_mem_addr, pdata->aux_mem_size, + pdata->aux_mem_src_perms); + + ret = qcom_scm_assign_mem(pdata->aux_mem_addr, pdata->aux_mem_size, + &pdata->aux_mem_src_perms, &hlos, 1); + if (ret) { + dev_err(dev, + "SCM aux unassign failed: addr=%pa size=%zu src_perms=0x%llx ret=%d\n", + &pdata->aux_mem_addr, pdata->aux_mem_size, + pdata->aux_mem_src_perms, ret); + return ret; + } + + pdata->aux_mem_assigned = false; + pdata->aux_mem_src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + dev_dbg(dev, "SCM aux unassign ok: addr=%pa\n", + &pdata->aux_mem_addr); + + return 0; +} + +static void q6apm_dai_release_reserved_memory(void *data) +{ + struct q6apm_dai_data *pdata = data; + + q6apm_dai_unassign_aux_memory(pdata); + q6apm_dai_unassign_reserved_memory(pdata); + of_reserved_mem_device_release(pdata->dev); +} + static const struct snd_pcm_hardware q6apm_dai_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -240,7 +514,7 @@ static void event_handler(uint32_t opcode, uint32_t token, void *payload, void * } static void event_handler_compr(uint32_t opcode, uint32_t token, - void *payload, void *priv) + void *payload, void *priv) { struct q6apm_dai_rtd *prtd = priv; struct snd_compr_stream *substream = prtd->cstream; @@ -296,8 +570,15 @@ static void event_handler_compr(uint32_t opcode, uint32_t token, } } +static int q6apm_dai_memory_unmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + +static int q6apm_dai_memory_map(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int graph_id, + size_t size); + static int q6apm_dai_prepare(struct snd_soc_component *component, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct q6apm_dai_rtd *prtd = runtime->private_data; @@ -305,6 +586,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, struct device *dev = component->dev; struct q6apm_dai_data *pdata; bool assigned_now = false; + bool reserved_mapped_now = false; int ret; pdata = snd_soc_component_get_drvdata(component); @@ -312,10 +594,49 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, return -EINVAL; if (!prtd || !prtd->graph) { - dev_err(dev, "%s: private data null or audio client freed\n", __func__); + dev_err(dev, "private data null or audio client freed\n"); return -EINVAL; } + if (pdata->use_reserved_mem && !prtd->memory_mapped) { + dev_dbg(dev, + "reserved prepare entry: assigned=%d mapped=%d dma=%pa phys=%pa pcm_size=%zu reserved=%pa/%zu graph=%d\n", + pdata->reserved_mem_assigned, prtd->memory_mapped, + &prtd->dma_addr, &prtd->phys, (size_t)prtd->pcm_size, + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + prtd->graph->id); + + ret = q6apm_dai_assign_aux_memory(pdata); + if (ret) { + dev_err(dev, "SCM aux assign failed at prepare: ret=%d\n", + ret); + return ret; + } + + if (!pdata->reserved_mem_assigned) { + ret = q6apm_dai_assign_reserved_memory(pdata); + if (ret) { + dev_err(dev, "SCM reserved assign failed at prepare: ret=%d\n", + ret); + return ret; + } + } + + dev_dbg(dev, + "reserved prepare: dma=%pa phys=%pa size=%zu graph=%d\n", + &prtd->dma_addr, &prtd->phys, (size_t)prtd->pcm_size, + prtd->graph->id); + + ret = q6apm_dai_memory_map(component, substream, + prtd->graph->id, prtd->pcm_size); + if (ret) + return ret; + prtd->memory_mapped = true; + reserved_mapped_now = true; + dev_dbg(dev, "reserved prepare map ok: graph=%d\n", + prtd->graph->id); + } + cfg.direction = substream->stream; cfg.sample_rate = runtime->rate; cfg.num_channels = runtime->channels; @@ -331,42 +652,73 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, prtd->pcm_count = snd_pcm_lib_period_bytes(substream); if (!prtd->scm_assigned) { ret = q6apm_dai_assign_memory(prtd, pdata); - if (ret) + if (ret) { + dev_err(dev, "SCM memory assign failed at prepare: ret=%d\n", + ret); return ret; + } assigned_now = prtd->scm_assigned; } /* rate and channels are sent to audio driver */ + dev_dbg(dev, + "media_format_shmem begin: graph=%d dir=%u rate=%u channels=%u bit_width=%u\n", + prtd->graph->id, cfg.direction, cfg.sample_rate, + cfg.num_channels, cfg.bit_width); ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); if (ret < 0) { - dev_err(dev, "%s: q6apm_open_write failed\n", __func__); + dev_err(dev, "q6apm_graph_media_format_shmem failed ret=%d\n", ret); goto err_unassign; } + dev_dbg(dev, "media_format_shmem success: graph=%d\n", + prtd->graph->id); + dev_dbg(dev, + "media_format_pcm begin: graph=%d dir=%u rate=%u channels=%u bit_width=%u\n", + prtd->graph->id, cfg.direction, cfg.sample_rate, + cfg.num_channels, cfg.bit_width); ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); - if (ret < 0) - dev_err(dev, "%s: CMD Format block failed\n", __func__); + if (ret < 0) { + dev_err(dev, "q6apm_graph_media_format_pcm failed ret=%d\n", ret); + goto err_unassign; + } + dev_dbg(dev, "media_format_pcm success: graph=%d\n", + prtd->graph->id); + dev_dbg(dev, + "alloc_fragments begin: graph=%d dir=%u phys=%pa period_size=%u periods=%u pcm_size=%u\n", + prtd->graph->id, substream->stream, &prtd->phys, + prtd->pcm_size / prtd->periods, prtd->periods, prtd->pcm_size); ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys, - (prtd->pcm_size / prtd->periods), prtd->periods); + (prtd->pcm_size / prtd->periods), prtd->periods); if (ret < 0) { - dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + dev_err(dev, "q6apm_alloc_fragments failed rc=%d\n", ret); ret = -ENOMEM; goto err_unassign; } + dev_dbg(dev, "alloc_fragments success: graph=%d\n", + prtd->graph->id); + dev_dbg(dev, "graph_prepare begin: graph=%d\n", + prtd->graph->id); ret = q6apm_graph_prepare(prtd->graph); if (ret) { - dev_err(dev, "Failed to prepare Graph %d\n", ret); + dev_err(dev, "q6apm_graph_prepare failed ret=%d\n", ret); goto err_unassign; } + dev_dbg(dev, "graph_prepare success: graph=%d\n", + prtd->graph->id); + dev_dbg(dev, "graph_start begin: graph=%d\n", + prtd->graph->id); ret = q6apm_graph_start(prtd->graph); if (ret) { - dev_err(dev, "Failed to Start Graph %d\n", ret); + dev_err(dev, "q6apm_graph_start failed ret=%d\n", ret); goto err_unassign; } + dev_dbg(dev, "graph_start success: graph=%d\n", + prtd->graph->id); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { int i; @@ -384,6 +736,11 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, err_unassign: if (assigned_now) q6apm_dai_unassign_memory(prtd, pdata); + if (reserved_mapped_now) { + dev_err(dev, "reserved prepare error: unmapping graph after ret=%d\n", ret); + if (!q6apm_dai_memory_unmap(component, substream)) + prtd->memory_mapped = false; + } return ret; } @@ -399,7 +756,7 @@ static int q6apm_dai_ack(struct snd_soc_component *component, struct snd_pcm_sub for (i = 0; i < avail_periods; i++) { ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP); if (ret < 0) { - dev_err(component->dev, "Error queuing playback buffer %d\n", ret); + dev_err(component->dev, "q6apm_write_async failed ret=%d\n", ret); return ret; } prtd->queue_ptr += runtime->period_size; @@ -452,7 +809,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, pdata = snd_soc_component_get_drvdata(component); if (!pdata) { - dev_err(dev, "Drv data not found ..\n"); + dev_err(dev, "Drv data not found\n"); return -EINVAL; } @@ -464,7 +821,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, prtd->substream = substream; prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id, substream->stream); if (IS_ERR(prtd->graph)) { - dev_err(dev, "%s: Could not allocate memory\n", __func__); + dev_err(dev, "q6apm_graph_open failed stream=%d\n", substream->stream); ret = PTR_ERR(prtd->graph); goto err; } @@ -473,19 +830,22 @@ static int q6apm_dai_open(struct snd_soc_component *component, runtime->hw = q6apm_dai_hardware_playback; else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) runtime->hw = q6apm_dai_hardware_capture; + runtime->hw.buffer_bytes_max = pdata->buffer_bytes_max; /* Ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { - dev_err(dev, "snd_pcm_hw_constraint_integer failed\n"); + dev_err(dev, "snd_pcm_hw_constraint_integer failed ret=%d\n", ret); goto err; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - BUFFER_BYTES_MIN, BUFFER_BYTES_MAX); + BUFFER_BYTES_MIN, + pdata->buffer_bytes_max); if (ret < 0) { - dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret); + dev_err(dev, "buffer bytes minmax constraint failed ret=%d max=%zu\n", + ret, pdata->buffer_bytes_max); goto err; } } @@ -493,18 +853,18 @@ static int q6apm_dai_open(struct snd_soc_component *component, /* setup 10ms latency to accommodate DSP restrictions */ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 480); if (ret < 0) { - dev_err(dev, "constraint for period bytes step ret = %d\n", ret); + dev_err(dev, "period size step constraint failed ret=%d\n", ret); goto err; } ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 480); if (ret < 0) { - dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret); + dev_err(dev, "buffer size step constraint failed ret=%d\n", ret); goto err; } runtime->private_data = prtd; - runtime->dma_bytes = BUFFER_BYTES_MAX; + runtime->dma_bytes = pdata->buffer_bytes_max; prtd->scm_assigned = false; prtd->dma_addr_valid = false; prtd->dma_addr = substream->dma_buffer.addr; @@ -533,14 +893,25 @@ static int q6apm_dai_close(struct snd_soc_component *component, q6apm_graph_stop(prtd->graph); q6apm_free_fragments(prtd->graph, substream->stream); } + + if (prtd->memory_mapped) { + ret = q6apm_dai_memory_unmap(component, substream); + if (ret) + dev_err(component->dev, + "close(): memory unmap failed graph=%d ret=%d\n", + prtd->graph->id, ret); + else + prtd->memory_mapped = false; + } + if (pdata && prtd->scm_assigned) { ret = q6apm_dai_unassign_memory(prtd, pdata); if (ret) { - dev_warn(component->dev, - "close(): VMID unassign failed for DMA buffer %pa: %d\n", + dev_err(component->dev, + "close(): VMID unassign failed for DMA buffer %pa: %d\n", &prtd->dma_addr, ret); WARN_ONCE(1, "q6apm-dai: SCM VMID unassign leak for DMA buffer %pa\n", - &prtd->dma_addr); + &prtd->dma_addr); } } @@ -567,12 +938,15 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component, } static int q6apm_dai_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai; struct q6apm_dai_rtd *prtd = runtime->private_data; struct q6apm_dai_data *pdata = snd_soc_component_get_drvdata(component); + dma_addr_t dma_addr; int ret; if (!pdata) @@ -581,18 +955,23 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, if (prtd->scm_assigned) { ret = q6apm_dai_unassign_memory(prtd, pdata); if (ret) { - dev_warn(component->dev, - "hw_params(): failed to release previous VMID assignment: %d\n", + dev_err(component->dev, + "hw_params(): failed to release previous VMID assignment: %d\n", ret); return ret; } } - if (!substream->dma_buffer.addr) + dma_addr = runtime->dma_addr ? runtime->dma_addr : substream->dma_buffer.addr; + if (!dma_addr) return -ENOMEM; - prtd->dma_addr = substream->dma_buffer.addr; + prtd->dma_addr = dma_addr; prtd->dma_addr_valid = true; + if (pdata->sid < 0) + prtd->phys = dma_addr; + else + prtd->phys = dma_addr | (pdata->sid << 32); prtd->pcm_size = params_buffer_bytes(params); prtd->periods = params_periods(params); @@ -608,6 +987,19 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, return -EINVAL; } + if (pdata->use_reserved_mem) { + if (!soc_prtd) + return -EINVAL; + + cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); + if (!cpu_dai) + return -EINVAL; + + dev_dbg(component->dev, + "reserved hw_params: dma=%pad size=%zu periods=%u\n", + &dma_addr, (size_t)prtd->pcm_size, prtd->periods); + } + return 0; } @@ -621,39 +1013,60 @@ static int q6apm_dai_hw_free(struct snd_soc_component *component, if (pdata && prtd->scm_assigned) { ret = q6apm_dai_unassign_memory(prtd, pdata); if (ret) - dev_warn(component->dev, - "hw_free(): VMID unassign failed for DMA buffer %pa: %d\n", + dev_err(component->dev, + "hw_free(): VMID unassign failed for DMA buffer %pa: %d\n", &prtd->dma_addr, ret); } - /* Fixed PCM DMA buffer is released in pcm_free(); close() retries unassign. */ + /* PCM DMA buffer is released by the ALSA managed buffer helpers. */ prtd->dma_addr_valid = false; return 0; } static int q6apm_dai_memory_map(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int graph_id) + struct snd_pcm_substream *substream, int graph_id, + size_t size) { struct q6apm_dai_data *pdata; struct device *dev = component->dev; + struct snd_pcm_runtime *runtime = substream->runtime; + dma_addr_t dma_addr; phys_addr_t phys; int ret; pdata = snd_soc_component_get_drvdata(component); if (!pdata) { - dev_err(component->dev, "Drv data not found ..\n"); + dev_err(component->dev, "Drv data not found\n"); return -EINVAL; } - if (pdata->sid < 0) - phys = substream->dma_buffer.addr; - else - phys = substream->dma_buffer.addr | (pdata->sid << 32); + if (pdata->use_reserved_mem) { + phys = pdata->reserved_mem_addr; + size = pdata->reserved_mem_size; + dev_dbg(dev, "reserved map begin: phys=%pa size=%zu graph=%d\n", + &phys, size, graph_id); + } else { + if (runtime && runtime->dma_addr) + dma_addr = runtime->dma_addr; + else + dma_addr = substream->dma_buffer.addr; + if (!dma_addr) + return -ENOMEM; - ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, BUFFER_BYTES_MAX); + if (pdata->sid < 0) + phys = dma_addr; + else + phys = dma_addr | (pdata->sid << 32); + } + + ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, size); if (ret < 0) - dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + dev_err(dev, "q6apm_map_memory_fixed_region failed rc=%d phys=%pa size=%zu graph=%d\n", + ret, &phys, size, graph_id); + else + dev_dbg(dev, "memory map done: phys=%pa size=%zu graph=%d\n", + &phys, size, graph_id); return ret; } @@ -662,32 +1075,48 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc { struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_pcm *pcm = rtd->pcm; - /* - * Allocate one extra page as a workaround for a DSP bug where 32-bit - * address arithmetic can overflow when the buffer is placed near the - * end of the addressable range. - */ - int size = BUFFER_BYTES_MAX + PAGE_SIZE; + struct q6apm_dai_data *pdata = snd_soc_component_get_drvdata(component); int graph_id, ret; struct snd_pcm_substream *substream; + if (!pdata) + return -EINVAL; + graph_id = cpu_dai->driver->id; - ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + if (pdata->use_reserved_mem) { + ret = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + component->dev, 0, + pdata->buffer_bytes_max); + if (ret) + return ret; + + return 0; + } + + /* + * Allocate one extra page as a workaround for a DSP bug where 32-bit + * address arithmetic can overflow when the buffer is placed near the + * end of the addressable range. + */ + ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, + BUFFER_BYTES_MAX + PAGE_SIZE); if (ret) return ret; /* Note: DSP backend dais are uni-directional ONLY(either playback or capture) */ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - ret = q6apm_dai_memory_map(component, substream, graph_id); + ret = q6apm_dai_memory_map(component, substream, graph_id, + BUFFER_BYTES_MAX); if (ret) return ret; } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - ret = q6apm_dai_memory_map(component, substream, graph_id); + ret = q6apm_dai_memory_map(component, substream, graph_id, + BUFFER_BYTES_MAX); if (ret) return ret; } @@ -695,23 +1124,32 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc return 0; } -static void q6apm_dai_memory_unmap(struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static int q6apm_dai_memory_unmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_prtd; struct snd_soc_dai *cpu_dai; int graph_id; + int ret; soc_prtd = snd_soc_substream_to_rtd(substream); if (!soc_prtd) - return; + return -EINVAL; cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); if (!cpu_dai) - return; + return -EINVAL; graph_id = cpu_dai->driver->id; - q6apm_unmap_memory_fixed_region(component->dev, graph_id); + dev_dbg(component->dev, "memory unmap begin: graph=%d\n", graph_id); + ret = q6apm_unmap_memory_fixed_region(component->dev, graph_id); + if (ret) + dev_err(component->dev, + "memory unmap failed: graph=%d ret=%d\n", graph_id, ret); + else + dev_dbg(component->dev, "memory unmap done: graph=%d\n", graph_id); + + return ret; } static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) @@ -719,12 +1157,16 @@ static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_p struct snd_pcm_substream *substream; substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - if (substream) + if (substream) { + dev_dbg(component->dev, "pcm_free(): capture memory unmap\n"); q6apm_dai_memory_unmap(component, substream); + } substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - if (substream) + if (substream) { + dev_dbg(component->dev, "pcm_free(): playback memory unmap\n"); q6apm_dai_memory_unmap(component, substream); + } } static int q6apm_dai_compr_open(struct snd_soc_component *component, @@ -1105,18 +1547,78 @@ static int q6apm_dai_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct q6apm_dai_data *pdata; struct of_phandle_args args; + struct resource reserved_mem; + size_t required_mem_size = max_t(size_t, BUFFER_BYTES_MIN, + CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE); + u32 src_vmid; int vmids; int rc; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; + pdata->dev = dev; + pdata->buffer_bytes_max = BUFFER_BYTES_MAX; + pdata->aux_mem_addr = Q6APM_STATIC_AUDIO_CARVEOUT_ADDR; + pdata->aux_mem_size = Q6APM_STATIC_AUDIO_CARVEOUT_SIZE; + pdata->aux_mem_src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + dev_dbg(dev, "probe: static aux memory addr=%pa size=%zu\n", + &pdata->aux_mem_addr, pdata->aux_mem_size); rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); if (rc < 0) pdata->sid = -1; else pdata->sid = args.args[0] & SID_MASK_DEFAULT; + dev_dbg(dev, "probe: sid=%lld iommus_rc=%d\n", pdata->sid, rc); + + if (of_property_present(node, "memory-region")) { + rc = of_reserved_mem_region_to_resource(node, 0, &reserved_mem); + if (rc) { + dev_err(dev, "failed to parse reserved memory region: %d\n", rc); + return rc; + } + + rc = of_reserved_mem_device_init_by_idx(dev, node, 0); + if (rc) { + dev_err(dev, "failed to attach reserved memory region: %d\n", rc); + return rc; + } + + pdata->use_reserved_mem = true; + pdata->reserved_mem_addr = reserved_mem.start; + pdata->reserved_mem_size = resource_size(&reserved_mem); + pdata->buffer_bytes_max = min_t(size_t, pdata->reserved_mem_size / 2, + BUFFER_BYTES_MAX); + pdata->buffer_bytes_max = min_t(size_t, pdata->buffer_bytes_max, + Q6APM_RESERVED_BUFFER_BYTES_MAX); + pdata->reserved_mem_src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + dev_dbg(dev, + "probe: reserved memory addr=%pa size=%zu buffer_bytes_max=%zu\n", + &pdata->reserved_mem_addr, pdata->reserved_mem_size, + pdata->buffer_bytes_max); + + rc = devm_add_action_or_reset(dev, + q6apm_dai_release_reserved_memory, pdata); + if (rc) + return rc; + } + + /* + * Default src_vmid to HLOS. Platforms where PCM DMA buffers reside in + * S2-only memory pre-owned by a non-HLOS VMID must override this via + * qcom,src-vmid so qcom_scm_assign_mem() receives the correct srcvm. + */ + pdata->src_vmid = QCOM_SCM_VMID_HLOS; + if (!of_property_read_u32(node, "qcom,src-vmid", &src_vmid)) { + if (src_vmid == 0 || src_vmid > Q6APM_SCM_MAX_VMID) { + dev_err(dev, "qcom,src-vmid=%u out of range [1..%u]\n", + src_vmid, Q6APM_SCM_MAX_VMID); + return -EINVAL; + } + pdata->src_vmid = src_vmid; + dev_dbg(dev, "probe: SCM src_vmid set to 0x%x from DT\n", src_vmid); + } vmids = of_property_count_u32_elems(node, "qcom,vmid"); if (vmids == -EINVAL) { @@ -1154,11 +1656,28 @@ static int q6apm_dai_probe(struct platform_device *pdev) } pdata->num_vmids = vmids; pdata->use_scm_assign = true; + dev_dbg(dev, "probe: SCM assign enabled src_vmid=0x%x num_dst=%d\n", + pdata->src_vmid, pdata->num_vmids); + for (rc = 0; rc < vmids; rc++) + dev_dbg(dev, "probe: SCM dst vmid[%d]=0x%x\n", rc, + pdata->vmids[rc]); } if (pdata->use_scm_assign && !qcom_scm_is_available()) return -EPROBE_DEFER; + if (pdata->use_reserved_mem && pdata->reserved_mem_size < required_mem_size) { + dev_err(dev, "reserved memory size %zu is smaller than required %zu\n", + pdata->reserved_mem_size, required_mem_size); + return -EINVAL; + } + + dev_dbg(dev, + "probe done: use_reserved=%d reserved=%pa/%zu use_scm=%d src_vmid=0x%x num_vmids=%d buffer_bytes_max=%zu\n", + pdata->use_reserved_mem, &pdata->reserved_mem_addr, + pdata->reserved_mem_size, pdata->use_scm_assign, + pdata->src_vmid, pdata->num_vmids, pdata->buffer_bytes_max); + dev_set_drvdata(dev, pdata); return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0); @@ -1183,3 +1702,4 @@ module_platform_driver(q6apm_dai_platform_driver); MODULE_DESCRIPTION("Q6APM dai driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF");