From 92af49e87926006867af64b1dd71560d4babab32 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 17 Jun 2026 13:53:16 +0530 Subject: [PATCH 1/6] FROMGIT: ASoC: qcom: q6apm: Add support for early buffer mapping on DSP Buffers are allocated on pcm_new and mapped in the dsp on every prepare call, which is inefficient and unnecessary. Add new functions q6apm_[un]map_memory_fixed_region to map it on to dsp only once after allocation. The push-pull / pos-buffer parts of the upstream commit are not included as that infrastructure does not exist in this 6.18 branch. Link: https://patch.msgid.link/20260402081118.348071-14-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Srinivas Kandagatla --- sound/soc/qcom/qdsp6/audioreach.c | 64 -------- sound/soc/qcom/qdsp6/audioreach.h | 5 +- sound/soc/qcom/qdsp6/q6apm-dai.c | 258 ++++++++++++++++++++++++++++-- sound/soc/qcom/qdsp6/q6apm.c | 159 +++++++++++------- sound/soc/qcom/qdsp6/q6apm.h | 14 +- 5 files changed, 358 insertions(+), 142 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 5bf1ddd4d587d..32dd98795e4da 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1449,70 +1449,6 @@ void audioreach_graph_free_buf(struct q6apm_graph *graph) } EXPORT_SYMBOL_GPL(audioreach_graph_free_buf); -int audioreach_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, size_t period_sz, - unsigned int periods, bool is_contiguous) -{ - struct apm_shared_map_region_payload *mregions; - struct apm_cmd_shared_mem_map_regions *cmd; - uint32_t num_regions, buf_sz, payload_size; - struct audioreach_graph_data *data; - struct gpr_pkt *pkt; - void *p; - int rc, i; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - data = &graph->rx_data; - else - data = &graph->tx_data; - - if (is_contiguous) { - num_regions = 1; - buf_sz = period_sz * periods; - } else { - buf_sz = period_sz; - num_regions = periods; - } - - /* DSP expects size should be aligned to 4K */ - buf_sz = ALIGN(buf_sz, 4096); - - payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions); - - pkt = audioreach_alloc_apm_pkt(payload_size, APM_CMD_SHARED_MEM_MAP_REGIONS, dir, - graph->port->id); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - - p = (void *)pkt + GPR_HDR_SIZE; - cmd = p; - cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; - cmd->num_regions = num_regions; - - cmd->property_flag = 0x0; - - mregions = p + sizeof(*cmd); - - mutex_lock(&graph->lock); - - for (i = 0; i < num_regions; i++) { - struct audio_buffer *ab; - - ab = &data->buf[i]; - mregions->shm_addr_lsw = lower_32_bits(ab->phys); - mregions->shm_addr_msw = upper_32_bits(ab->phys); - mregions->mem_size_bytes = buf_sz; - ++mregions; - } - mutex_unlock(&graph->lock); - - rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); - - kfree(pkt); - - return rc; -} -EXPORT_SYMBOL_GPL(audioreach_map_memory_regions); - int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) { struct data_cmd_wr_sh_mem_ep_eos *eos; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 290f8f60bb467..50b2953811305 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -720,6 +720,7 @@ struct audioreach_connection { struct audioreach_graph_info { int id; + u32 mem_map_handle; uint32_t num_sub_graphs; struct list_head sg_list; /* DPCM connection from FE Graph to BE graph */ @@ -861,10 +862,6 @@ int audioreach_tplg_init(struct snd_soc_component *component); /* Module specific */ void audioreach_graph_free_buf(struct q6apm_graph *graph); -int audioreach_map_memory_regions(struct q6apm_graph *graph, - unsigned int dir, size_t period_sz, - unsigned int periods, - bool is_contiguous); 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); diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 54c1a33f9f937..a32efa256d29e 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "q6apm.h" @@ -33,6 +34,8 @@ #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 SID_MASK_DEFAULT 0xF static const struct snd_compr_codec_caps q6apm_compr_caps = { @@ -78,10 +81,106 @@ struct q6apm_dai_rtd { bool notify_on_drain; }; +struct q6apm_scm_region { + phys_addr_t dma_addr; + unsigned int size; + u64 src_perms; + bool assigned; +}; + struct q6apm_dai_data { long long sid; + int num_vmids; + u32 vmids[Q6APM_MAX_VMIDS]; + bool use_scm_assign; + struct q6apm_scm_region scm_regions[SNDRV_PCM_STREAM_LAST + 1]; }; +static int q6apm_dai_assign_memory(struct snd_pcm_substream *substream, + struct q6apm_dai_data *pdata) +{ + struct q6apm_scm_region *scm_region = &pdata->scm_regions[substream->stream]; + struct qcom_scm_vmperm *dst_vmids; + int dst_count = 0; + int ret; + int i; + + if (!pdata->use_scm_assign || pdata->num_vmids <= 0 || scm_region->assigned) + return 0; + + if (!substream->dma_buffer.addr) + return -ENOMEM; + + dst_vmids = kcalloc(pdata->num_vmids + 1, sizeof(*dst_vmids), GFP_KERNEL); + if (!dst_vmids) + return -ENOMEM; + + /* Always keep HLOS RW so CPU can continue buffer access. */ + 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++) { + /* + * 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)) + continue; + + dst_vmids[dst_count].vmid = pdata->vmids[i]; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + } + + /* Nothing to assign beyond HLOS access. */ + if (dst_count == 1) { + kfree(dst_vmids); + return 0; + } + + scm_region->dma_addr = substream->dma_buffer.addr; + scm_region->size = ALIGN(BUFFER_BYTES_MAX, PAGE_SIZE); + scm_region->src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + + ret = qcom_scm_assign_mem(scm_region->dma_addr, scm_region->size, + &scm_region->src_perms, dst_vmids, dst_count); + kfree(dst_vmids); + if (ret) + return ret; + + scm_region->assigned = true; + return 0; +} + +static int q6apm_dai_unassign_memory(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct q6apm_dai_data *pdata) +{ + struct q6apm_scm_region *scm_region = &pdata->scm_regions[substream->stream]; + struct qcom_scm_vmperm hlos = { + .vmid = QCOM_SCM_VMID_HLOS, + .perm = QCOM_SCM_PERM_RW, + }; + struct device *dev = component->dev; + int ret; + + if (!pdata->use_scm_assign || !scm_region->assigned) + return 0; + + ret = qcom_scm_assign_mem(scm_region->dma_addr, scm_region->size, + &scm_region->src_perms, &hlos, 1); + if (!ret) { + scm_region->assigned = false; + scm_region->src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + } else { + dev_err(dev, "Failed to unassign DMA buffer %pa from VMIDs: %d\n", + &scm_region->dma_addr, ret); + } + + return ret; +} + 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 | @@ -238,7 +337,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, if (prtd->state) { /* clear the previous setup if any */ q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, substream->stream); + q6apm_free_fragments(prtd->graph, substream->stream); } prtd->pcm_count = snd_pcm_lib_period_bytes(substream); @@ -253,9 +352,8 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, if (ret < 0) dev_err(dev, "%s: CMD Format block failed\n", __func__); - ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys, - (prtd->pcm_size / prtd->periods), prtd->periods); - + ret = q6apm_alloc_fragments(prtd->graph, substream->stream, prtd->phys, + (prtd->pcm_size / prtd->periods), prtd->periods); if (ret < 0) { dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); return -ENOMEM; @@ -424,7 +522,7 @@ static int q6apm_dai_close(struct snd_soc_component *component, if (prtd->state) { /* only stop graph that is started */ q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, substream->stream); + q6apm_free_fragments(prtd->graph, substream->stream); } q6apm_graph_close(prtd->graph); @@ -473,11 +571,107 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, return 0; } +static void 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) +{ + struct q6apm_dai_data *pdata; + struct device *dev = component->dev; + phys_addr_t phys; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) { + 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); + + ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, BUFFER_BYTES_MAX); + if (ret < 0) + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + + return ret; +} + static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_pcm *pcm = rtd->pcm; int size = BUFFER_BYTES_MAX; + int graph_id, ret; + struct snd_pcm_substream *substream; + + graph_id = cpu_dai->driver->id; + + ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, 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); + 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); + if (ret) + return ret; + } + + return 0; +} + +static void 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; - return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + soc_prtd = snd_soc_substream_to_rtd(substream); + if (!soc_prtd) + return; + + cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); + if (!cpu_dai) + return; + + graph_id = cpu_dai->driver->id; + q6apm_unmap_memory_fixed_region(component->dev, graph_id); +} + +static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) +{ + struct q6apm_dai_data *pdata = snd_soc_component_get_drvdata(component); + struct snd_pcm_substream *substream; + + if (!pdata) + return; + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + if (pdata->use_scm_assign) + q6apm_dai_unassign_memory(component, substream, pdata); + q6apm_dai_memory_unmap(component, substream); + } + + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (substream) { + if (pdata->use_scm_assign) + q6apm_dai_unassign_memory(component, substream, pdata); + q6apm_dai_memory_unmap(component, substream); + } } static int q6apm_dai_compr_open(struct snd_soc_component *component, @@ -535,7 +729,8 @@ static int q6apm_dai_compr_free(struct snd_soc_component *component, struct q6apm_dai_rtd *prtd = runtime->private_data; q6apm_graph_stop(prtd->graph); - q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_free_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_unmap_memory_fixed_region(component->dev, prtd->graph->id); q6apm_graph_close(prtd->graph); snd_dma_free_pages(&prtd->dma_buffer); prtd->graph = NULL; @@ -688,9 +883,9 @@ static int q6apm_dai_compr_set_params(struct snd_soc_component *component, if (ret) return ret; - ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, - prtd->phys, (prtd->pcm_size / prtd->periods), - prtd->periods); + ret = q6apm_alloc_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, + prtd->phys, (prtd->pcm_size / prtd->periods), + prtd->periods); if (ret < 0) return -ENOMEM; @@ -846,6 +1041,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .close = q6apm_dai_close, .prepare = q6apm_dai_prepare, .pcm_construct = q6apm_dai_pcm_new, + .pcm_destruct = q6apm_dai_pcm_free, .hw_params = q6apm_dai_hw_params, .pointer = q6apm_dai_pointer, .trigger = q6apm_dai_trigger, @@ -861,6 +1057,7 @@ 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; + int vmids; int rc; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -873,6 +1070,47 @@ static int q6apm_dai_probe(struct platform_device *pdev) else pdata->sid = args.args[0] & SID_MASK_DEFAULT; + vmids = of_property_count_u32_elems(node, "qcom,vmid"); + if (vmids == -EINVAL) { + pdata->num_vmids = 0; + pdata->use_scm_assign = false; + } else if (vmids < 0) { + return vmids; + } else if (vmids == 0) { + dev_err(dev, "qcom,vmid must contain at least one VMID\n"); + return -EINVAL; + } else if (vmids > Q6APM_MAX_VMIDS) { + dev_err(dev, "qcom,vmid: %d VMIDs exceeds maximum of %d\n", + vmids, Q6APM_MAX_VMIDS); + return -EINVAL; + } + + if (vmids > 0) { + int i; + + rc = of_property_read_u32_array(node, "qcom,vmid", + pdata->vmids, vmids); + if (rc) + return rc; + for (i = 0; i < vmids; i++) { + if (pdata->vmids[i] == QCOM_SCM_VMID_HLOS) { + dev_err(dev, "qcom,vmid must not include HLOS VMID (%u)\n", + QCOM_SCM_VMID_HLOS); + return -EINVAL; + } + if (pdata->vmids[i] > Q6APM_SCM_MAX_VMID) { + dev_err(dev, "qcom,vmid[%d]=%u exceeds SCM max VMID %u\n", + i, pdata->vmids[i], Q6APM_SCM_MAX_VMID); + return -EINVAL; + } + } + pdata->num_vmids = vmids; + pdata->use_scm_assign = true; + } + + if (pdata->use_scm_assign && !qcom_scm_is_available()) + return -EPROBE_DEFER; + dev_set_drvdata(dev, pdata); return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0); diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index f32af288edd21..bb01211d4a9d0 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -210,13 +210,54 @@ int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, } EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem); -int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys, - size_t period_sz, unsigned int periods) +int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phys_addr_t phys, + size_t sz) +{ + struct audioreach_graph_info *info; + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct apm_shared_map_region_payload *mregions; + struct apm_cmd_shared_mem_map_regions *cmd; + int payload_size = sizeof(*cmd) + (sizeof(*mregions)); + u32 buf_sz; + void *p; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, + APM_CMD_SHARED_MEM_MAP_REGIONS, graph_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + info = idr_find(&apm->graph_info_idr, graph_id); + if (!info) + return -ENODEV; + + if (info->mem_map_handle) + return 0; + + /* DSP expects size should be aligned to 4K */ + buf_sz = ALIGN(sz, 4096); + + p = (void *)pkt + GPR_HDR_SIZE; + cmd = p; + cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = 1; + cmd->property_flag = 0x0; + + mregions = p + sizeof(*cmd); + + mregions->shm_addr_lsw = lower_32_bits(phys); + mregions->shm_addr_msw = upper_32_bits(phys); + mregions->mem_size_bytes = buf_sz; + + return q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); +} +EXPORT_SYMBOL_GPL(q6apm_map_memory_fixed_region); + +int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, + phys_addr_t phys, size_t period_sz, + unsigned int periods) { struct audioreach_graph_data *data; struct audio_buffer *buf; int cnt; - int rc; if (dir == SNDRV_PCM_STREAM_PLAYBACK) data = &graph->rx_data; @@ -258,47 +299,41 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a mutex_unlock(&graph->lock); - rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1); - if (rc < 0) { - dev_err(graph->dev, "Memory_map_regions failed\n"); - audioreach_graph_free_buf(graph); - } - - return rc; + return 0; } -EXPORT_SYMBOL_GPL(q6apm_map_memory_regions); +EXPORT_SYMBOL_GPL(q6apm_alloc_fragments); -int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) +int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) { struct apm_cmd_shared_mem_unmap_regions *cmd; - struct audioreach_graph_data *data; - struct gpr_pkt *pkt; - int rc; + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph_info *info; + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), + APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - data = &graph->rx_data; - else - data = &graph->tx_data; + info = idr_find(&apm->graph_info_idr, graph_id); + if (!info) + return -ENODEV; - if (!data->mem_map_handle) + if (!info->mem_map_handle) return 0; - pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir, - graph->port->id); - if (IS_ERR(pkt)) - return PTR_ERR(pkt); - cmd = (void *)pkt + GPR_HDR_SIZE; - cmd->mem_map_handle = data->mem_map_handle; + cmd->mem_map_handle = info->mem_map_handle; - rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); - kfree(pkt); + return q6apm_send_cmd_sync(apm, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); +} +EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region); +int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir) +{ audioreach_graph_free_buf(graph); - return rc; + return 0; } -EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions); +EXPORT_SYMBOL_GPL(q6apm_free_fragments); int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) { @@ -442,7 +477,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, write_buffer->buf_size = len; write_buffer->timestamp_lsw = lsw_ts; write_buffer->timestamp_msw = msw_ts; - write_buffer->mem_map_handle = graph->rx_data.mem_map_handle; + write_buffer->mem_map_handle = graph->info->mem_map_handle; write_buffer->flags = wflags; graph->rx_data.dsp_buf++; @@ -482,7 +517,7 @@ int q6apm_read(struct q6apm_graph *graph) read_buffer->buf_addr_lsw = lower_32_bits(ab->phys); read_buffer->buf_addr_msw = upper_32_bits(ab->phys); - read_buffer->mem_map_handle = port->mem_map_handle; + read_buffer->mem_map_handle = graph->info->mem_map_handle; read_buffer->buf_size = ab->size; port->dsp_buf++; @@ -516,10 +551,9 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) { struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done; - struct apm_cmd_rsp_shared_mem_map_regions *rsp; - struct gpr_ibasic_rsp_result_t *result; + const struct gpr_ibasic_rsp_result_t *result; struct q6apm_graph *graph = priv; - struct gpr_hdr *hdr = &data->hdr; + const struct gpr_hdr *hdr = &data->hdr; struct device *dev = graph->dev; uint32_t client_event; phys_addr_t phys; @@ -551,18 +585,6 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) done->buf_addr_msw); } - break; - case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS: - graph->result.opcode = hdr->opcode; - graph->result.status = 0; - rsp = data->payload; - - if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK) - graph->rx_data.mem_map_handle = rsp->mem_map_handle; - else - graph->tx_data.mem_map_handle = rsp->mem_map_handle; - - wake_up(&graph->cmd_wait); break; case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2: if (!graph->ar_graph) @@ -593,16 +615,6 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { - case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - graph->result.opcode = result->opcode; - graph->result.status = 0; - if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK) - graph->rx_data.mem_map_handle = 0; - else - graph->tx_data.mem_map_handle = 0; - - wake_up(&graph->cmd_wait); - break; case APM_CMD_SHARED_MEM_MAP_REGIONS: case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT: case APM_CMD_SET_CFG: @@ -800,10 +812,12 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; + struct audioreach_graph_info *info; struct q6apm *apm = dev_get_drvdata(&gdev->dev); + struct apm_cmd_rsp_shared_mem_map_regions *rsp; struct device *dev = &gdev->dev; struct gpr_ibasic_rsp_result_t *result; - struct gpr_hdr *hdr = &data->hdr; + const struct gpr_hdr *hdr = &data->hdr; result = data->payload; @@ -817,6 +831,7 @@ static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op) break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { + case APM_CMD_SHARED_MEM_MAP_REGIONS: case APM_CMD_GRAPH_START: case APM_CMD_GRAPH_OPEN: case APM_CMD_GRAPH_PREPARE: @@ -831,10 +846,38 @@ static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op) result->opcode); 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 + dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, + result->opcode); + + wake_up(&apm->wait); + break; default: break; } break; + case APM_CMD_RSP_SHARED_MEM_MAP_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 = rsp->mem_map_handle; + else + dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, + result->opcode); + + wake_up(&apm->wait); + break; default: break; } diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 7ce08b401e310..bfb6167eba78f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -77,7 +77,6 @@ struct audioreach_graph_data { struct audio_buffer *buf; uint32_t num_periods; uint32_t dsp_buf; - uint32_t mem_map_handle; atomic_t hw_ptr; }; @@ -132,11 +131,14 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags); /* Memory Map related */ -int q6apm_map_memory_regions(struct q6apm_graph *graph, - unsigned int dir, phys_addr_t phys, - size_t period_sz, unsigned int periods); -int q6apm_unmap_memory_regions(struct q6apm_graph *graph, - unsigned int dir); +int q6apm_map_memory_fixed_region(struct device *dev, + unsigned int graph_id, + phys_addr_t phys, size_t sz); +int q6apm_alloc_fragments(struct q6apm_graph *graph, + unsigned int dir, phys_addr_t phys, + size_t period_sz, unsigned int periods); +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, struct gpr_pkt *pkt, uint32_t rsp_opcode); From cc270bd4970b3d70b5c19583e81ebdb29ca47f81 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Wed, 17 Jun 2026 13:54:17 +0530 Subject: [PATCH 2/6] FROMLIST: ASoC: qcom: q6apm-dai: add VMID-based SCM assignment Add optional VMID-based SCM memory assignment for q6apm fixed PCM DMA uffers when qcom,vmid is provided in DT. On platforms where audio processing runs outside HLOS, LPASS PCM DMA buffers must be shared with secure/non-HLOS VMIDs so both endpoints can access the same payload memory. Keep HLOS as an RW owner and grant configured destination VMIDs RW access so both playback and capture data paths remain functional. Assign and unassign SCM permissions at fixed-buffer lifetime boundaries: assign in pcm_new() after fixed buffer allocation/map, and restore HLOS ownership in pcm_free() before unmap. Link: https://lore.kernel.org/all/20260609064038.492641-3-ajay.nandam@oss.qualcomm.com/ Signed-off-by: Ajay Kumar Nandam --- sound/soc/qcom/Kconfig | 1 + sound/soc/qcom/qdsp6/q6apm-dai.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 2eca25478fd45..3b893bbfb03b9 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -112,6 +112,7 @@ config SND_SOC_QDSP6_ASM_DAI config SND_SOC_QDSP6_APM_DAI tristate + select QCOM_SCM select SND_SOC_COMPRESS config SND_SOC_QDSP6_APM_LPASS_DAI diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index a32efa256d29e..e690e4da59a76 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -603,11 +603,15 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component, static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct q6apm_dai_data *pdata = snd_soc_component_get_drvdata(component); struct snd_pcm *pcm = rtd->pcm; int size = BUFFER_BYTES_MAX; 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); @@ -620,6 +624,13 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc ret = q6apm_dai_memory_map(component, substream, graph_id); if (ret) return ret; + if (pdata->use_scm_assign) { + ret = q6apm_dai_assign_memory(substream, pdata); + if (ret) { + q6apm_dai_memory_unmap(component, substream); + return ret; + } + } } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { @@ -627,6 +638,13 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc ret = q6apm_dai_memory_map(component, substream, graph_id); if (ret) return ret; + if (pdata->use_scm_assign) { + ret = q6apm_dai_assign_memory(substream, pdata); + if (ret) { + q6apm_dai_memory_unmap(component, substream); + return ret; + } + } } return 0; From 81e16b92d949e12c7b6cf7e6bdfe552ef049f590 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Wed, 17 Jun 2026 14:17:12 +0530 Subject: [PATCH 3/6] FROMLIST: ASoC: qcom: qdsp6: generalize GPR service domain Use each GPR domain advertised by the qcom APR/GPR layer when building APM/PRM packets and add explicit dest_domain parameters to all AudioReach alloc helpers. Legacy helpers fall back to ADSP while graphs, media-format, and shared-memory packets reuse gdev->domain_id for modem targets. Also fix q6prm_set_hw_core_req() which was left with the old 5-argument audioreach_alloc_cmd_pkt() call and a stray duplicate call appended outside any function after MODULE_LICENSE. Replace with the correct 6-argument form passing audioreach_gpr_dest_domain(gdev) and remove the stray lines. Link: https://lore.kernel.org/all/20260609064038.492641-4-ajay.nandam@oss.qualcomm.com/ Co-developed-by: Pratyush Meduri Signed-off-by: Pratyush Meduri Signed-off-by: Ajay Kumar Nandam --- sound/soc/qcom/qdsp6/audioreach.c | 91 ++++++++++++++++++++++--------- sound/soc/qcom/qdsp6/audioreach.h | 23 ++++---- sound/soc/qcom/qdsp6/q6apm.c | 42 ++++++++++---- sound/soc/qcom/qdsp6/q6prm.c | 10 +++- 4 files changed, 115 insertions(+), 51 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 32dd98795e4da..7dfcde0789898 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -217,7 +217,8 @@ struct apm_display_port_module_intf_cfg { #define APM_DP_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_display_port_module_intf_cfg), 8) static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token, - uint32_t src_port, uint32_t dest_port, bool has_cmd_hdr) + u32 src_port, u32 dest_port, u16 dest_domain, + bool has_cmd_hdr) { struct gpr_pkt *pkt; void *p; @@ -237,7 +238,10 @@ static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t pkt->hdr.dest_port = dest_port; pkt->hdr.src_port = src_port; - pkt->hdr.dest_domain = GPR_DOMAIN_ID_ADSP; + if (!dest_domain) + dest_domain = GPR_DOMAIN_ID_ADSP; + + pkt->hdr.dest_domain = dest_domain; pkt->hdr.src_domain = GPR_DOMAIN_ID_APPS; pkt->hdr.token = token; pkt->hdr.opcode = opcode; @@ -254,30 +258,34 @@ static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t } void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token, - uint32_t src_port, uint32_t dest_port) + u32 src_port, u32 dest_port, u16 dest_domain) { - return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, false); + return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, + dest_domain, false); } EXPORT_SYMBOL_GPL(audioreach_alloc_pkt); -void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token, uint32_t src_port) +void *audioreach_alloc_apm_pkt(int pkt_size, u32 opcode, u32 token, + u32 src_port, u16 dest_domain) { return __audioreach_alloc_pkt(pkt_size, opcode, token, src_port, APM_MODULE_INSTANCE_ID, - false); + dest_domain, false); } EXPORT_SYMBOL_GPL(audioreach_alloc_apm_pkt); void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode, uint32_t token, - uint32_t src_port, uint32_t dest_port) + u32 src_port, u32 dest_port, u16 dest_domain) { - return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, true); + return __audioreach_alloc_pkt(payload_size, opcode, token, src_port, dest_port, + dest_domain, true); } EXPORT_SYMBOL_GPL(audioreach_alloc_cmd_pkt); -void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token) +void *audioreach_alloc_apm_cmd_pkt(int pkt_size, u32 opcode, u32 token, + u16 dest_domain) { return __audioreach_alloc_pkt(pkt_size, opcode, token, GPR_APM_MODULE_IID, - APM_MODULE_INSTANCE_ID, true); + APM_MODULE_INSTANCE_ID, dest_domain, true); } EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt); @@ -472,6 +480,7 @@ void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info int num_sub_graphs = 0; int num_modules = 0; int num_modules_list; + u16 dest_domain = audioreach_gpr_dest_domain(apm->gdev); struct gpr_pkt *pkt; void *p; @@ -506,7 +515,8 @@ void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info mc_sz = APM_MOD_CONN_PSIZE(mcon, num_connections); payload_size = sg_sz + cont_sz + ml_sz + mp_sz + mc_sz; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_GRAPH_OPEN, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_GRAPH_OPEN, 0, + dest_domain); if (IS_ERR(pkt)) return pkt; @@ -630,6 +640,7 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph, int rc, payload_size; struct gpr_pkt *pkt; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); ic_sz = APM_DP_INTF_CFG_PSIZE; ep_sz = APM_HW_EP_CFG_PSIZE; @@ -638,7 +649,8 @@ static int audioreach_display_port_set_media_format(struct q6apm_graph *graph, payload_size = ic_sz + ep_sz + fs_sz + dl_sz; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -698,6 +710,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, int rc, payload_size; struct gpr_pkt *pkt; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); ic_sz = APM_CDMA_INTF_CFG_PSIZE; ep_sz = APM_HW_EP_CFG_PSIZE; @@ -707,7 +720,8 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, payload_size = ic_sz + ep_sz + fs_sz + pm_sz + dl_sz; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -770,9 +784,11 @@ int audioreach_send_u32_param(struct q6apm_graph *graph, struct audioreach_modul uint32_t *param; int rc, payload_size; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE; - p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(p)) return -ENOMEM; @@ -826,15 +842,17 @@ static int audioreach_gapless_set_media_format(struct q6apm_graph *graph, } static int audioreach_set_module_config(struct q6apm_graph *graph, - struct audioreach_module *module, - struct audioreach_module_config *cfg) + struct audioreach_module *module, + struct audioreach_module_config *cfg) { int payload_size = le32_to_cpu(module->data->size); struct gpr_pkt *pkt; int rc; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -860,11 +878,13 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, struct gpr_pkt *pkt; int rc, i; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + APM_MODULE_PARAM_DATA_SIZE; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -979,12 +999,13 @@ int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_modu struct gpr_pkt *pkt; int iid, payload_size, rc; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); payload_size = sizeof(struct apm_sh_module_media_fmt_cmd); iid = q6apm_graph_get_rx_shmem_module_iid(graph); pkt = audioreach_alloc_cmd_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT, - 0, graph->port->id, iid); + 0, graph->port->id, iid, dest_domain); if (IS_ERR(pkt)) return -ENOMEM; @@ -1016,6 +1037,7 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, int rc, payload_size; struct gpr_pkt *pkt; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); ic_sz = APM_I2S_INTF_CFG_PSIZE; ep_sz = APM_HW_EP_CFG_PSIZE; @@ -1023,7 +1045,8 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, payload_size = ic_sz + ep_sz + fs_sz; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1103,7 +1126,10 @@ static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; void *p; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1168,9 +1194,11 @@ static int audioreach_logging_set_media_format(struct q6apm_graph *graph, int rc, payload_size; struct gpr_pkt *pkt; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); payload_size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1205,6 +1233,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, struct apm_module_param_data *param_data; int rc, payload_size; struct gpr_pkt *pkt; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); @@ -1213,7 +1242,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels); - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1257,6 +1287,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, int rc, payload_size; struct gpr_pkt *pkt; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); if (num_channels > 4) { dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); @@ -1266,7 +1297,8 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels) + APM_MODULE_PARAM_DATA_SIZE; pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, - graph->port->id, module->instance_id); + graph->port->id, module->instance_id, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1317,9 +1349,11 @@ int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *mo int rc, payload_size; struct gpr_pkt *pkt; void *p; + u16 dest_domain = audioreach_gpr_dest_domain(apm->gdev); payload_size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1348,9 +1382,11 @@ static int audioreach_gain_set(struct q6apm_graph *graph, struct audioreach_modu struct apm_gain_module_cfg *cfg; int rc, payload_size; struct gpr_pkt *pkt; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); payload_size = APM_GAIN_CFG_PSIZE; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1454,10 +1490,11 @@ int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) struct data_cmd_wr_sh_mem_ep_eos *eos; struct gpr_pkt *pkt; int rc = 0, iid; + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); iid = q6apm_graph_get_rx_shmem_module_iid(graph); pkt = audioreach_alloc_cmd_pkt(sizeof(*eos), DATA_CMD_WR_SH_MEM_EP_EOS, 0, - graph->port->id, iid); + graph->port->id, iid, dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 50b2953811305..0493eb1c73f93 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -844,17 +844,20 @@ 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, u32 opcode, u32 token, + u16 dest_domain); 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); -void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, - uint32_t token, uint32_t src_port, - uint32_t dest_port); +void *audioreach_alloc_cmd_pkt(int payload_size, u32 opcode, u32 token, + u32 src_port, u32 dest_port, u16 dest_domain); +void *audioreach_alloc_apm_pkt(int pkt_size, u32 opcode, u32 token, + u32 src_port, u16 dest_domain); +void *audioreach_alloc_pkt(int payload_size, u32 opcode, u32 token, + u32 src_port, u32 dest_port, u16 dest_domain); void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct audioreach_graph_info *info); /* Topology specific */ diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index bb01211d4a9d0..15c3ddb031819 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -104,7 +104,10 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs); - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0); + u16 dest_domain = audioreach_gpr_dest_domain(apm->gdev); + + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -150,7 +153,10 @@ static int q6apm_get_apm_state(struct q6apm *apm) { struct gpr_pkt *pkt; - pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0); + u16 dest_domain = audioreach_gpr_dest_domain(apm->gdev); + + pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -220,8 +226,11 @@ int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phy int payload_size = sizeof(*cmd) + (sizeof(*mregions)); u32 buf_sz; void *p; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, - APM_CMD_SHARED_MEM_MAP_REGIONS, graph_id); + u16 dest_domain = audioreach_gpr_dest_domain(apm->gdev); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_cmd_pkt(payload_size, + APM_CMD_SHARED_MEM_MAP_REGIONS, + graph_id, dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -308,8 +317,11 @@ int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id) struct apm_cmd_shared_mem_unmap_regions *cmd; struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), - APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + u16 dest_domain = audioreach_gpr_dest_domain(apm->gdev); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), + APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id, + dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -461,9 +473,13 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, int rc, iid; iid = q6apm_graph_get_rx_shmem_module_iid(graph); - pkt = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, - graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), - graph->port->id, iid); + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); + + pkt = audioreach_alloc_pkt(sizeof(*write_buffer), + DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, + graph->rx_data.dsp_buf | + (len << APM_WRITE_TOKEN_LEN_SHIFT), + graph->port->id, iid, dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -504,8 +520,12 @@ int q6apm_read(struct q6apm_graph *graph) int rc, iid; iid = q6apm_graph_get_tx_shmem_module_iid(graph); - pkt = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, - graph->tx_data.dsp_buf, graph->port->id, iid); + u16 dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev); + + pkt = audioreach_alloc_pkt(sizeof(*read_buffer), + DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, + graph->tx_data.dsp_buf, + graph->port->id, iid, dest_domain); if (IS_ERR(pkt)) return PTR_ERR(pkt); diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index 81554d2026589..b4029f829ea35 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -73,7 +73,9 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC; } - pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID); + pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, + GPR_PRM_MODULE_IID, + audioreach_gpr_dest_domain(gdev)); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -120,7 +122,8 @@ static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_att int rc; pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id, - GPR_PRM_MODULE_IID); + GPR_PRM_MODULE_IID, + audioreach_gpr_dest_domain(gdev)); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -157,7 +160,8 @@ static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_att int rc; pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id, - GPR_PRM_MODULE_IID); + GPR_PRM_MODULE_IID, + audioreach_gpr_dest_domain(gdev)); if (IS_ERR(pkt)) return PTR_ERR(pkt); From 1363d26aba65aaded0e7c1b6f8d5e02fd8e04184 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Thu, 18 Jun 2026 01:22:58 +0530 Subject: [PATCH 4/6] FROMLIST: dt-bindings: sound: qcom,q6apm-dai: add memory-region property Document the new memory-region property that allows one or more reserved-memory carveout regions to be SCM-assigned to the VMIDs listed in qcom,vmid. - Add memory-region as an optional phandle-array (1-8 entries). Each entry must point to a shared-dma-pool, no-map reserved-memory node. Index 0 is the control-path buffer; subsequent entries are data-path buffers. - Enforce via dependentRequired that memory-region is only valid when qcom,vmid is also present. - Expand qcom,vmid description to mention carveout regions and the qcom_scm_is_available() probe-defer requirement. - Add a second example showing both properties in use with two carveout regions and two destination VMIDs. Link: https://lore.kernel.org/all/20260618113509.2025881-2-ajay.nandam@oss.qualcomm.com/ Signed-off-by: Ajay Kumar Nandam --- .../bindings/sound/qcom,q6apm-dai.yaml | 36 ++++++++++++++++--- .../bindings/sound/qcom,q6apm-lpass-dais.yaml | 9 +++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml index b767625985a74..1c0ea0f389e44 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml @@ -10,7 +10,11 @@ maintainers: - Srinivas Kandagatla description: | - This binding describes the Qualcomm APM DAIs in DSP + This binding describes the Qualcomm APM DAIs in DSP. + When qcom,vmid is present, the driver performs SCM memory + assignment for PCM DMA buffers and any reserved-memory regions + listed in memory-region, granting the specified VMIDs RW access + while retaining HLOS as a co-owner. properties: compatible: @@ -20,9 +24,21 @@ properties: minItems: 1 maxItems: 2 + memory-region: + description: + List of phandles to reserved-memory regions (shared-dma-pool, + no-map) that must be SCM-assigned to the VMIDs in qcom,vmid. + The first entry is the control-path buffer; subsequent entries + are data-path buffers. Only meaningful when qcom,vmid is present. + minItems: 1 + maxItems: 8 + qcom,vmid: - description: Optional list of destination VMIDs to share PCM DMA buffers with. - HLOS retains RW access as source owner and must not be listed. + description: + Optional list of destination VMIDs to grant RW access to PCM DMA + buffers and any memory-region carveouts. HLOS retains RW access + as source owner and must not be listed. When present, + qcom_scm_is_available() must return true at probe time. $ref: /schemas/types.yaml#/definitions/uint32-array items: minimum: 1 @@ -32,7 +48,10 @@ properties: required: - compatible - - iommus + +dependentRequired: + memory-region: + - qcom,vmid additionalProperties: false @@ -42,3 +61,12 @@ examples: compatible = "qcom,q6apm-dais"; iommus = <&apps_smmu 0x1801 0x0>; }; + - | + #include + dais { + compatible = "qcom,q6apm-dais"; + iommus = <&apps_smmu 0x0c01 0x0>; + /* index 0: control path, index 1: data path */ + memory-region = <&audio_heap_mem &audio_mdsp_carveout_mem>; + qcom,vmid = ; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml index 0eae1c382f5e0..2c3d4ba30013b 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml @@ -49,14 +49,13 @@ patternProperties: clock-names: minItems: 1 + maxItems: 3 items: - - const: mclk - - const: bclk - - const: eclk + enum: [mclk, bclk, eclk] dependencies: - clocks: [ clock-names ] - clock-names: [ clocks ] + clocks: [clock-names] + clock-names: [clocks] required: - reg From e598d57293b5aa6ba8a5344b91933f71e2664305 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Thu, 18 Jun 2026 16:31:15 +0530 Subject: [PATCH 5/6] FROMLIST: arm64: dts: qcom: shikra: Add MDSP carveout memory and update APM DAIs memory regions Add a dedicated MDSP carveout memory region for audio usecases on Shikra and mark both existing audio heap and MDSP carveout regions as shared DMA pools. Update the Q6 APM DAI node to reference multiple memory regions, where index 0 is used for control path buffers and index 1 is used for MDSP data path buffers. This separation ensures proper memory allocation and access for APM communication between APSS and MDSP. Also add shared-dma-pool compatibility to the existing audio heap region to align with upstream DMA pool usage. Link: https://lore.kernel.org/all/20260618113509.2025881-3-ajay.nandam@oss.qualcomm.com/ Signed-off-by: Ajay Kumar Nandam --- arch/arm64/boot/dts/qcom/shikra.dtsi | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/shikra.dtsi b/arch/arm64/boot/dts/qcom/shikra.dtsi index b8b3b111c2b15..17b581a1631c6 100644 --- a/arch/arm64/boot/dts/qcom/shikra.dtsi +++ b/arch/arm64/boot/dts/qcom/shikra.dtsi @@ -380,7 +380,14 @@ }; audio_heap_mem: audio-heap@86200000 { - reg = <0x0 0x86200000 0x0 0x100000>; + compatible = "shared-dma-pool"; + reg = <0x0 0x86200000 0x0 0x40000>; + no-map; + }; + + audio_mdsp_carveout_mem: audio-mdsp-carveout@86240000 { + compatible = "shared-dma-pool"; + reg = <0x0 0x86240000 0x0 0x100000>; no-map; }; @@ -2634,6 +2641,8 @@ q6apmdai: dais { compatible = "qcom,q6apm-dais"; + memory-region = <&audio_heap_mem + &audio_mdsp_carveout_mem>; qcom,vmid = ; }; From 9fc912af0e06003042b0d27b909098295b107d92 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Thu, 18 Jun 2026 16:28:22 +0530 Subject: [PATCH 6/6] FROMLIST: ASoC: qcom: q6apm-dai: add carveout SCM assignment for mDSP buffers On targets like Shikra where audio processing runs on the mDSP (modem DSP) instead of the ADSP, the DSP operates with stage-2 secured memory. Unlike ADSP platforms where SMMU-mapped system RAM is directly accessible, mDSP cannot access buffers unless they are explicitly SCM-assigned to the appropriate VMIDs. This requires allocating PCM DMA buffers from reserved carveout regions and performing SCM assignment to grant both HLOS and the mDSP shared RW access. Add support for reserved-memory carveout regions listed in the memory-region DT property of the q6apm-dais node. When qcom,vmid is configured, all carveout regions are SCM-assigned to HLOS + the configured destination VMIDs (RW) at pcm_new() time and ownership is restored to HLOS-only at pcm_free(). For the data-path region (memory-region index 1), the device DMA pool is attached via of_reserved_mem_device_init_by_idx() so that snd_pcm_set_managed_buffer_all() allocates PCM buffers directly from the carveout. The pool size is read from the DT node and capped at BUFFER_BYTES_MAX. All paths are gated on use_scm_assign and has_reserved_mem so existing platforms without memory-region or qcom,vmid in DT are completely unaffected. Link: https://lore.kernel.org/all/20260618112810.2009847-1-ajay.nandam@oss.qualcomm.com/ Signed-off-by: Ajay Kumar Nandam --- sound/soc/qcom/qdsp6/q6apm-dai.c | 206 ++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 3 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index e690e4da59a76..585372a741995 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "q6apm.h" @@ -34,8 +35,9 @@ #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 31 +#define Q6APM_MAX_VMIDS 8 +#define Q6APM_MAX_CARVEOUTS 8 #define SID_MASK_DEFAULT 0xF static const struct snd_compr_codec_caps q6apm_compr_caps = { @@ -94,6 +96,16 @@ struct q6apm_dai_data { u32 vmids[Q6APM_MAX_VMIDS]; bool use_scm_assign; struct q6apm_scm_region scm_regions[SNDRV_PCM_STREAM_LAST + 1]; + /* + * carveout regions from memory-region DT property + * (index 0: control path, index 1+: data path) + */ + struct q6apm_scm_region carveout_regions[Q6APM_MAX_CARVEOUTS]; + int num_carveouts; + /* true when memory-region DT property is present and DMA pool attached */ + bool has_reserved_mem; + /* size of the data-path reserved region, capped at BUFFER_BYTES_MAX */ + size_t reserved_buf_size; }; static int q6apm_dai_assign_memory(struct snd_pcm_substream *substream, @@ -181,6 +193,102 @@ static int q6apm_dai_unassign_memory(struct snd_soc_component *component, return ret; } +static int q6apm_dai_assign_one_region(struct q6apm_scm_region *region, + struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm *dst_vmids; + int dst_count = 0; + int ret, i; + + if (region->assigned) + return 0; + + dst_vmids = kcalloc(pdata->num_vmids + 1, sizeof(*dst_vmids), + GFP_KERNEL); + if (!dst_vmids) + return -ENOMEM; + + /* Always keep HLOS RW so CPU can continue carveout access. */ + 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 (WARN_ON_ONCE(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) { + /* Nothing to assign beyond HLOS access. */ + kfree(dst_vmids); + return 0; + } + + ret = qcom_scm_assign_mem(region->dma_addr, region->size, + ®ion->src_perms, dst_vmids, dst_count); + kfree(dst_vmids); + if (!ret) + region->assigned = true; + return ret; +} + +static int q6apm_dai_assign_carveout(struct q6apm_dai_data *pdata) +{ + int i, ret; + + if (!pdata->use_scm_assign || !pdata->num_carveouts) + return 0; + + for (i = 0; i < pdata->num_carveouts; i++) { + ret = q6apm_dai_assign_one_region(&pdata->carveout_regions[i], + pdata); + if (ret) + return ret; + } + return 0; +} + +static void q6apm_dai_unassign_one_region(struct snd_soc_component *component, + struct q6apm_scm_region *region) +{ + struct device *dev = component->dev; + struct qcom_scm_vmperm hlos = { + .vmid = QCOM_SCM_VMID_HLOS, + .perm = QCOM_SCM_PERM_RW, + }; + int ret; + + if (!region->assigned) + return; + + ret = qcom_scm_assign_mem(region->dma_addr, region->size, + ®ion->src_perms, &hlos, 1); + if (!ret) { + region->assigned = false; + region->src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + } else { + dev_err(dev, + "Failed to unassign carveout %pa from VMIDs: %d\n", + ®ion->dma_addr, ret); + } +} + +static void q6apm_dai_unassign_carveout(struct snd_soc_component *component, + struct q6apm_dai_data *pdata) +{ + int i; + + if (!pdata->use_scm_assign || !pdata->num_carveouts) + return; + + for (i = 0; i < pdata->num_carveouts; i++) + q6apm_dai_unassign_one_region(component, + &pdata->carveout_regions[i]); +} + 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 | @@ -614,7 +722,19 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc graph_id = cpu_dai->driver->id; - ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); + /* + * When a reserved DMA pool is attached (memory-region in DT), allocate + * PCM buffers from it so the DSP accesses the carveout address directly. + * Fall back to the standard fixed system-RAM buffer on other platforms. + */ + if (pdata->has_reserved_mem) + ret = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + component->dev, + pdata->reserved_buf_size, + pdata->reserved_buf_size); + else + ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + component->dev, size); if (ret) return ret; @@ -647,6 +767,12 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc } } + if (pdata->use_scm_assign && pdata->num_carveouts) { + ret = q6apm_dai_assign_carveout(pdata); + if (ret) + return ret; + } + return 0; } @@ -677,6 +803,9 @@ static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_p if (!pdata) return; + if (pdata->use_scm_assign && pdata->num_carveouts) + q6apm_dai_unassign_carveout(component, pdata); + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; if (substream) { if (pdata->use_scm_assign) @@ -1126,6 +1255,77 @@ static int q6apm_dai_probe(struct platform_device *pdev) pdata->use_scm_assign = true; } + /* + * Attach the data-path reserved memory region (index 1 in + * memory-region, e.g. audio_mdsp_carveout_mem on shikra) as a DMA + * pool so that snd_pcm_set_managed_buffer_all() allocates PCM + * buffers from the carveout instead of system RAM. The size is read + * from the DT node and capped at BUFFER_BYTES_MAX. + * Index 0 is the control-path carveout (SCM-assigned separately). + * Platforms without memory-region are completely unaffected. + */ + if (of_property_present(node, "memory-region")) { + struct device_node *rmem_node; + struct reserved_mem *rmem = NULL; + + /* index 1 = data path (PCM DMA buffer pool) */ + rmem_node = of_parse_phandle(node, "memory-region", 1); + if (rmem_node) { + rmem = of_reserved_mem_lookup(rmem_node); + of_node_put(rmem_node); + } + + if (rmem) { + rc = of_reserved_mem_device_init_by_idx(dev, node, 1); + if (rc) { + dev_err(dev, + "failed to attach reserved memory pool: %d\n", + rc); + return rc; + } + rc = devm_add_action_or_reset(dev, + (void (*)(void *)) + of_reserved_mem_device_release, + dev); + if (rc) + return rc; + pdata->reserved_buf_size = min_t(size_t, rmem->size, + BUFFER_BYTES_MAX); + pdata->has_reserved_mem = true; + } else { + dev_warn(dev, + "memory-region index 1 not found, using system RAM\n"); + } + } + + if (pdata->use_scm_assign) { + struct device_node *mem_node; + int idx = 0; + + while ((mem_node = of_parse_phandle(node, "memory-region", + idx++))) { + struct reserved_mem *rmem; + struct q6apm_scm_region *r; + + if (pdata->num_carveouts >= Q6APM_MAX_CARVEOUTS) { + dev_warn(dev, + "memory-region: too many entries, ignoring rest\n"); + of_node_put(mem_node); + break; + } + + rmem = of_reserved_mem_lookup(mem_node); + of_node_put(mem_node); + if (!rmem) + continue; + + r = &pdata->carveout_regions[pdata->num_carveouts++]; + r->dma_addr = rmem->base; + r->size = ALIGN(rmem->size, PAGE_SIZE); + r->src_perms = BIT_ULL(QCOM_SCM_VMID_HLOS); + } + } + if (pdata->use_scm_assign && !qcom_scm_is_available()) return -EPROBE_DEFER;