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