mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
ASoC: qcom: enable audio on stage-2 protected DSPs (mDSP)
Ajay Kumar Nandam <ajay.nandam@oss.qualcomm.com> says: On platforms such as Qualcomm Shikra, audio is served by the modem DSP (mDSP) rather than the ADSP. The mDSP runs in a stage-2 protected context and cannot use the SMMU, so the PCM buffers it consumes must live in reserved-memory carveouts that are handed to the consumer VMIDs via a hypervisor (SCM) memory assignment. This series adds that access model to the q6apm DAI driver and its binding, alongside the existing stage-1/SMMU (iommus) path, which is left untouched. The driver detects the mDSP target at runtime from the GPR domain_id (GPR_DOMAIN_ID_MODEM) and enables the SCM assignment path automatically. VMIDs are hardcoded in the driver (HLOS + MSS_MSA + LPASS) rather than read from DT, following the consensus from v2 review discussion with Krzysztof Kozlowski and Srinivas Kandagatla. Tested on Qualcomm Shikra with mDSP audio playback and capture. Link: https://patch.msgid.link/20260923-vmid-v4-v6-0-3d61a0319b1c@oss.qualcomm.com
This commit is contained in:
commit
76f97899ec
|
|
@ -20,9 +20,24 @@ properties:
|
|||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
memory-region:
|
||||
minItems: 1
|
||||
items:
|
||||
- description:
|
||||
Control-path buffer (audio_heap_mem) for platforms where the DSP
|
||||
runs in a stage-2 protected context (e.g. modem-DSP).
|
||||
- description:
|
||||
Data-path buffer pool from which per-stream DMA buffers are
|
||||
allocated.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- iommus
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- iommus
|
||||
- required:
|
||||
- memory-region
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ config SND_SOC_QDSP6_ASM_DAI
|
|||
config SND_SOC_QDSP6_APM_DAI
|
||||
tristate
|
||||
select SND_SOC_COMPRESS
|
||||
select QCOM_SCM
|
||||
|
||||
config SND_SOC_QDSP6_APM_LPASS_DAI
|
||||
tristate
|
||||
|
|
|
|||
|
|
@ -593,10 +593,10 @@ EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt);
|
|||
int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
|
||||
struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock,
|
||||
gpr_port_t *port, wait_queue_head_t *cmd_wait,
|
||||
const struct gpr_pkt *pkt, uint32_t rsp_opcode)
|
||||
struct gpr_pkt *pkt, uint32_t rsp_opcode)
|
||||
{
|
||||
|
||||
const struct gpr_hdr *hdr = &pkt->hdr;
|
||||
struct gpr_hdr *hdr = &pkt->hdr;
|
||||
int rc;
|
||||
|
||||
mutex_lock(cmd_lock);
|
||||
|
|
@ -636,10 +636,12 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(audioreach_send_cmd_sync);
|
||||
|
||||
int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt,
|
||||
int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt,
|
||||
uint32_t rsp_opcode)
|
||||
{
|
||||
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
|
||||
|
||||
return audioreach_send_cmd_sync(graph->dev, NULL, &graph->result, &graph->lock,
|
||||
graph->port, &graph->cmd_wait, pkt, rsp_opcode);
|
||||
}
|
||||
|
|
@ -994,6 +996,8 @@ int audioreach_compr_set_param(struct q6apm_graph *graph,
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
|
||||
|
||||
return gpr_send_port_pkt(graph->port, pkt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(audioreach_compr_set_param);
|
||||
|
|
@ -1609,6 +1613,8 @@ int audioreach_shared_memory_send_eos(struct q6apm_graph *graph)
|
|||
|
||||
eos->policy = WR_SH_MEM_EP_EOS_POLICY_LAST;
|
||||
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
|
||||
|
||||
return gpr_send_port_pkt(graph->port, pkt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(audioreach_shared_memory_send_eos);
|
||||
|
|
|
|||
|
|
@ -1002,14 +1002,19 @@ struct audioreach_module_config {
|
|||
};
|
||||
|
||||
/* Packet Allocation routines */
|
||||
void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t
|
||||
token);
|
||||
static inline u16 audioreach_gpr_dest_domain(gpr_device_t *gdev)
|
||||
{
|
||||
return gdev && gdev->domain_id ? gdev->domain_id : GPR_DOMAIN_ID_ADSP;
|
||||
}
|
||||
|
||||
void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode,
|
||||
uint32_t token);
|
||||
void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels);
|
||||
void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode,
|
||||
uint32_t token, uint32_t src_port,
|
||||
uint32_t dest_port);
|
||||
void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
|
||||
uint32_t src_port);
|
||||
uint32_t src_port);
|
||||
void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
|
||||
uint32_t token, uint32_t src_port,
|
||||
uint32_t dest_port);
|
||||
|
|
@ -1020,10 +1025,13 @@ int audioreach_tplg_init(struct snd_soc_component *component);
|
|||
|
||||
/* Module specific */
|
||||
void audioreach_graph_free_buf(struct q6apm_graph *graph);
|
||||
int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result,
|
||||
struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait,
|
||||
const struct gpr_pkt *pkt, uint32_t rsp_opcode);
|
||||
int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt,
|
||||
int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev,
|
||||
struct gpr_ibasic_rsp_result_t *result,
|
||||
struct mutex *cmd_lock, gpr_port_t *port,
|
||||
wait_queue_head_t *cmd_wait,
|
||||
struct gpr_pkt *pkt, uint32_t rsp_opcode);
|
||||
int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph,
|
||||
struct gpr_pkt *pkt,
|
||||
uint32_t rsp_opcode);
|
||||
int audioreach_set_media_format(struct q6apm_graph *graph,
|
||||
const struct audioreach_module *module,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2021, Linaro Limited
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <dt-bindings/firmware/qcom,scm.h>
|
||||
#include <dt-bindings/soc/qcom,gpr.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/firmware/qcom/qcom_scm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/dma.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "q6apm.h"
|
||||
|
||||
#define DRV_NAME "q6apm-dai"
|
||||
|
|
@ -36,6 +41,16 @@
|
|||
#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)
|
||||
#define SID_MASK_DEFAULT 0xF
|
||||
|
||||
#define Q6APM_MAX_SCM_REGIONS 16
|
||||
#define Q6APM_POOL_MAX_STREAMS 8
|
||||
|
||||
struct q6apm_scm_region {
|
||||
phys_addr_t addr;
|
||||
size_t size;
|
||||
u64 src_perms;
|
||||
bool assigned;
|
||||
};
|
||||
|
||||
static const struct snd_compr_codec_caps q6apm_compr_caps = {
|
||||
.num_descriptors = 1,
|
||||
.descriptor[0].max_ch = 2,
|
||||
|
|
@ -84,9 +99,103 @@ struct q6apm_dai_rtd {
|
|||
};
|
||||
|
||||
struct q6apm_dai_data {
|
||||
struct device *dev;
|
||||
long long sid;
|
||||
bool use_scm_assign;
|
||||
bool has_reserved_mem;
|
||||
size_t reserved_buf_size;
|
||||
struct mutex scm_lock; /* protects scm_regions and num_scm_regions */
|
||||
struct q6apm_scm_region scm_regions[Q6APM_MAX_SCM_REGIONS];
|
||||
int num_scm_regions;
|
||||
};
|
||||
|
||||
static int q6apm_dai_scm_assign(struct q6apm_dai_data *pdata,
|
||||
phys_addr_t addr, size_t size)
|
||||
{
|
||||
struct qcom_scm_vmperm dst[] = {
|
||||
{ .vmid = QCOM_SCM_VMID_HLOS, .perm = QCOM_SCM_PERM_RW },
|
||||
{ .vmid = QCOM_SCM_VMID_MSS_MSA, .perm = QCOM_SCM_PERM_RW },
|
||||
{ .vmid = QCOM_SCM_VMID_LPASS, .perm = QCOM_SCM_PERM_RW },
|
||||
};
|
||||
struct q6apm_scm_region *r;
|
||||
u64 src = BIT(QCOM_SCM_VMID_HLOS);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pdata->scm_lock);
|
||||
|
||||
if (pdata->num_scm_regions >= Q6APM_MAX_SCM_REGIONS) {
|
||||
mutex_unlock(&pdata->scm_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
ret = qcom_scm_assign_mem(addr, size, &src, dst, ARRAY_SIZE(dst));
|
||||
if (ret) {
|
||||
mutex_unlock(&pdata->scm_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
r = &pdata->scm_regions[pdata->num_scm_regions++];
|
||||
r->addr = addr;
|
||||
r->size = size;
|
||||
r->src_perms = src;
|
||||
r->assigned = true;
|
||||
|
||||
mutex_unlock(&pdata->scm_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void q6apm_dai_scm_unassign(struct q6apm_dai_data *pdata,
|
||||
phys_addr_t addr)
|
||||
{
|
||||
struct qcom_scm_vmperm hlos = {
|
||||
.vmid = QCOM_SCM_VMID_HLOS,
|
||||
.perm = QCOM_SCM_PERM_RW,
|
||||
};
|
||||
int i;
|
||||
|
||||
mutex_lock(&pdata->scm_lock);
|
||||
|
||||
for (i = 0; i < pdata->num_scm_regions; i++) {
|
||||
if (pdata->scm_regions[i].addr != addr ||
|
||||
!pdata->scm_regions[i].assigned)
|
||||
continue;
|
||||
|
||||
if (qcom_scm_assign_mem(addr, pdata->scm_regions[i].size,
|
||||
&pdata->scm_regions[i].src_perms,
|
||||
&hlos, 1)) {
|
||||
dev_err(pdata->dev, "SCM unassign %pa failed\n", &addr);
|
||||
mutex_unlock(&pdata->scm_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
pdata->scm_regions[i].assigned = false;
|
||||
pdata->num_scm_regions--;
|
||||
pdata->scm_regions[i] = pdata->scm_regions[pdata->num_scm_regions];
|
||||
mutex_unlock(&pdata->scm_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock(&pdata->scm_lock);
|
||||
}
|
||||
|
||||
static void q6apm_dai_scm_cleanup(void *data)
|
||||
{
|
||||
struct q6apm_dai_data *pdata = data;
|
||||
int i;
|
||||
|
||||
for (i = pdata->num_scm_regions - 1; i >= 0; i--) {
|
||||
if (pdata->scm_regions[i].assigned)
|
||||
q6apm_dai_scm_unassign(pdata,
|
||||
pdata->scm_regions[i].addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void q6apm_dai_reserved_mem_release(void *data)
|
||||
{
|
||||
of_reserved_mem_device_release(data);
|
||||
}
|
||||
|
||||
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 |
|
||||
|
|
@ -409,8 +518,11 @@ static int q6apm_dai_open(struct snd_soc_component *component,
|
|||
}
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
size_t buf_max = pdata->has_reserved_mem ? pdata->reserved_buf_size :
|
||||
BUFFER_BYTES_MAX;
|
||||
|
||||
ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
BUFFER_BYTES_MIN, BUFFER_BYTES_MAX);
|
||||
BUFFER_BYTES_MIN, buf_max);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret);
|
||||
goto err;
|
||||
|
|
@ -431,17 +543,20 @@ static int q6apm_dai_open(struct snd_soc_component *component,
|
|||
}
|
||||
|
||||
runtime->private_data = prtd;
|
||||
runtime->dma_bytes = BUFFER_BYTES_MAX;
|
||||
runtime->dma_bytes = pdata->has_reserved_mem ? pdata->reserved_buf_size :
|
||||
BUFFER_BYTES_MAX;
|
||||
if (pdata->sid < 0)
|
||||
prtd->phys = substream->dma_buffer.addr;
|
||||
else
|
||||
prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
|
||||
|
||||
if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) {
|
||||
size_t buf_max = pdata->has_reserved_mem ? pdata->reserved_buf_size :
|
||||
BUFFER_BYTES_MAX;
|
||||
void *pos_buffer;
|
||||
|
||||
prtd->pos_phys = prtd->phys + BUFFER_BYTES_MAX;
|
||||
pos_buffer = (void *)(substream->dma_buffer.area + BUFFER_BYTES_MAX);
|
||||
prtd->pos_phys = prtd->phys + buf_max;
|
||||
pos_buffer = (void *)(substream->dma_buffer.area + buf_max);
|
||||
prtd->pos_buffer = (struct sh_mem_pull_push_mode_position_buffer *)(pos_buffer);
|
||||
}
|
||||
|
||||
|
|
@ -535,6 +650,7 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component,
|
|||
{
|
||||
struct q6apm_dai_data *pdata;
|
||||
struct device *dev = component->dev;
|
||||
size_t buf_max;
|
||||
phys_addr_t phys;
|
||||
int ret;
|
||||
|
||||
|
|
@ -544,20 +660,23 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf_max = pdata->has_reserved_mem ? pdata->reserved_buf_size :
|
||||
BUFFER_BYTES_MAX;
|
||||
|
||||
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);
|
||||
ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, buf_max);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret);
|
||||
|
||||
if (is_push_pull) {
|
||||
if (pdata->sid < 0)
|
||||
phys = substream->dma_buffer.addr + BUFFER_BYTES_MAX;
|
||||
phys = substream->dma_buffer.addr + buf_max;
|
||||
else
|
||||
phys = (substream->dma_buffer.addr + BUFFER_BYTES_MAX) | (pdata->sid << 32);
|
||||
phys = (substream->dma_buffer.addr + buf_max) | (pdata->sid << 32);
|
||||
|
||||
ret = q6apm_map_pos_buffer(dev, graph_id, phys, POS_BUFFER_BYTES);
|
||||
if (ret < 0)
|
||||
|
|
@ -572,20 +691,22 @@ 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;
|
||||
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;
|
||||
int graph_id, ret;
|
||||
bool is_push_pull;
|
||||
struct snd_pcm_substream *substream = NULL;
|
||||
|
||||
pdata = snd_soc_component_get_drvdata(component);
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->has_reserved_mem)
|
||||
size = pdata->reserved_buf_size + PAGE_SIZE;
|
||||
|
||||
graph_id = cpu_dai->driver->id;
|
||||
|
||||
/* 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;
|
||||
else if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
|
||||
|
|
@ -603,9 +724,24 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata->use_scm_assign && !pdata->has_reserved_mem) {
|
||||
ret = q6apm_dai_scm_assign(pdata,
|
||||
substream->dma_buffer.addr,
|
||||
ALIGN(size, PAGE_SIZE));
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"SCM assign buffer failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = q6apm_dai_memory_map(component, substream, graph_id, is_push_pull);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (pdata->use_scm_assign && !pdata->has_reserved_mem)
|
||||
q6apm_dai_scm_unassign(pdata,
|
||||
substream->dma_buffer.addr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -635,15 +771,26 @@ static void q6apm_dai_memory_unmap(struct snd_soc_component *component,
|
|||
|
||||
static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm)
|
||||
{
|
||||
struct q6apm_dai_data *pdata;
|
||||
struct snd_pcm_substream *substream;
|
||||
|
||||
pdata = snd_soc_component_get_drvdata(component);
|
||||
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||
if (substream)
|
||||
if (substream) {
|
||||
q6apm_dai_memory_unmap(component, substream);
|
||||
if (pdata && pdata->use_scm_assign && !pdata->has_reserved_mem)
|
||||
q6apm_dai_scm_unassign(pdata,
|
||||
substream->dma_buffer.addr);
|
||||
}
|
||||
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
if (substream)
|
||||
if (substream) {
|
||||
q6apm_dai_memory_unmap(component, substream);
|
||||
if (pdata && pdata->use_scm_assign && !pdata->has_reserved_mem)
|
||||
q6apm_dai_scm_unassign(pdata,
|
||||
substream->dma_buffer.addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int q6apm_dai_compr_open(struct snd_soc_component *component,
|
||||
|
|
@ -683,6 +830,17 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata->use_scm_assign && !pdata->has_reserved_mem) {
|
||||
ret = q6apm_dai_scm_assign(pdata, prtd->dma_buffer.addr,
|
||||
ALIGN(size, PAGE_SIZE));
|
||||
if (ret) {
|
||||
dev_err(dev, "SCM assign compr buffer failed: %d\n",
|
||||
ret);
|
||||
snd_dma_free_pages(&prtd->dma_buffer);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->sid < 0)
|
||||
prtd->phys = prtd->dma_buffer.addr;
|
||||
else
|
||||
|
|
@ -700,11 +858,16 @@ static int q6apm_dai_compr_free(struct snd_soc_component *component,
|
|||
{
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct q6apm_dai_rtd *prtd = runtime->private_data;
|
||||
struct q6apm_dai_data *pdata;
|
||||
|
||||
pdata = snd_soc_component_get_drvdata(component);
|
||||
|
||||
q6apm_graph_stop(prtd->graph);
|
||||
q6apm_free_fragments(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
q6apm_unmap_memory_fixed_region(component->dev, prtd->graph->id);
|
||||
q6apm_graph_close(prtd->graph);
|
||||
if (pdata && pdata->use_scm_assign && !pdata->has_reserved_mem)
|
||||
q6apm_dai_scm_unassign(pdata, prtd->dma_buffer.addr);
|
||||
snd_dma_free_pages(&prtd->dma_buffer);
|
||||
prtd->graph = NULL;
|
||||
kfree(prtd);
|
||||
|
|
@ -1021,6 +1184,7 @@ static int q6apm_dai_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct q6apm *apm = dev_get_drvdata(dev->parent);
|
||||
struct q6apm_dai_data *pdata;
|
||||
struct of_phandle_args args;
|
||||
int rc;
|
||||
|
|
@ -1029,12 +1193,123 @@ static int q6apm_dai_probe(struct platform_device *pdev)
|
|||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->dev = dev;
|
||||
mutex_init(&pdata->scm_lock);
|
||||
|
||||
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;
|
||||
|
||||
if (apm && apm->gdev &&
|
||||
apm->gdev->domain_id == GPR_DOMAIN_ID_MODEM) {
|
||||
if (!qcom_scm_is_available())
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (pdata->sid >= 0) {
|
||||
dev_err(dev,
|
||||
"iommus and mDSP SCM path are mutually exclusive\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata->use_scm_assign = true;
|
||||
|
||||
rc = devm_add_action_or_reset(dev, q6apm_dai_scm_cleanup,
|
||||
pdata);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (pdata->use_scm_assign) {
|
||||
int mem_count;
|
||||
|
||||
mem_count = of_count_phandle_with_args(node, "memory-region",
|
||||
NULL);
|
||||
if (mem_count >= 1) {
|
||||
struct device_node *mem_node;
|
||||
struct reserved_mem *rmem;
|
||||
|
||||
mem_node = of_parse_phandle(node, "memory-region", 0);
|
||||
rmem = of_reserved_mem_lookup(mem_node);
|
||||
of_node_put(mem_node);
|
||||
if (!rmem) {
|
||||
dev_err(dev,
|
||||
"memory-region[0]: lookup failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = q6apm_dai_scm_assign(pdata, rmem->base,
|
||||
ALIGN(rmem->size, PAGE_SIZE));
|
||||
if (rc) {
|
||||
dev_err(dev,
|
||||
"SCM assign memory-region[0] failed: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (mem_count >= 2) {
|
||||
struct device_node *mem_node;
|
||||
struct reserved_mem *rmem;
|
||||
size_t per_stream;
|
||||
|
||||
mem_node = of_parse_phandle(node, "memory-region", 1);
|
||||
rmem = of_reserved_mem_lookup(mem_node);
|
||||
of_node_put(mem_node);
|
||||
if (!rmem) {
|
||||
dev_err(dev,
|
||||
"memory-region[1]: lookup failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
per_stream = rmem->size / Q6APM_POOL_MAX_STREAMS;
|
||||
if (per_stream <= POS_BUFFER_BYTES + PAGE_SIZE) {
|
||||
dev_err(dev,
|
||||
"reserved-memory pool too small: %pa bytes\n",
|
||||
&rmem->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign the whole pool to the consumer VMIDs once here,
|
||||
* instead of per-stream in pcm_new()/compr_open(). Each
|
||||
* qcom_scm_assign_mem() call consumes an entry in a small
|
||||
* fixed-size TZ memory-protection table shared platform-wide;
|
||||
* assigning per-stream-slice (up to Q6APM_POOL_MAX_STREAMS
|
||||
* times) exhausts that table and hangs the SMC call.
|
||||
*/
|
||||
rc = q6apm_dai_scm_assign(pdata, rmem->base,
|
||||
ALIGN(rmem->size, PAGE_SIZE));
|
||||
if (rc) {
|
||||
dev_err(dev,
|
||||
"SCM assign memory-region[1] failed: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_reserved_mem_device_init_by_idx(dev, node, 1);
|
||||
if (rc) {
|
||||
dev_err(dev,
|
||||
"reserved-memory pool init failed: %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = devm_add_action_or_reset(dev,
|
||||
q6apm_dai_reserved_mem_release,
|
||||
dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pdata->reserved_buf_size =
|
||||
min_t(size_t,
|
||||
per_stream - POS_BUFFER_BYTES - PAGE_SIZE,
|
||||
BUFFER_BYTES_MAX);
|
||||
pdata->has_reserved_mem = true;
|
||||
}
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, pdata);
|
||||
|
||||
return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0);
|
||||
|
|
|
|||
|
|
@ -29,11 +29,13 @@ struct apm_graph_mgmt_cmd {
|
|||
|
||||
static struct q6apm *g_apm;
|
||||
|
||||
int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt,
|
||||
int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
|
||||
uint32_t rsp_opcode)
|
||||
{
|
||||
gpr_device_t *gdev = apm->gdev;
|
||||
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(gdev);
|
||||
|
||||
return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
|
||||
NULL, &apm->wait, pkt, rsp_opcode);
|
||||
}
|
||||
|
|
@ -502,6 +504,8 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
|
|||
|
||||
mutex_unlock(&graph->lock);
|
||||
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
|
||||
|
||||
return gpr_send_port_pkt(graph->port, pkt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(q6apm_write_async);
|
||||
|
|
@ -536,6 +540,8 @@ int q6apm_read(struct q6apm_graph *graph)
|
|||
|
||||
mutex_unlock(&graph->lock);
|
||||
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(graph->apm->gdev);
|
||||
|
||||
return gpr_send_port_pkt(graph->port, pkt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(q6apm_read);
|
||||
|
|
@ -900,6 +906,7 @@ static int apm_probe(gpr_device_t *gdev)
|
|||
|
||||
static void apm_remove(gpr_device_t *gdev)
|
||||
{
|
||||
g_apm = NULL;
|
||||
of_platform_depopulate(&gdev->dev);
|
||||
snd_soc_unregister_component(&gdev->dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph,
|
|||
int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir);
|
||||
int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id);
|
||||
/* Helpers */
|
||||
int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt,
|
||||
int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
|
||||
uint32_t rsp_opcode);
|
||||
|
||||
/* Callback for graph specific */
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ struct prm_cmd_release_rsc {
|
|||
|
||||
static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
|
||||
{
|
||||
pkt->hdr.dest_domain = audioreach_gpr_dest_domain(prm->gdev);
|
||||
|
||||
return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
|
||||
NULL, &prm->wait, pkt, rsp_opcode);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user