ASoC: qcom: qdsp6: add push/pull module support

Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> says:

This patchset adds support for Push/Pull mode modules.
Push-pull mode uses dedicated shared-memory modules that allow the DSP
to access the PCM circular buffer directly. In addition to reducing
fragment queueing and ACK handling in the host driver,
This mode exposes a DSP-maintained position buffer that provides
fine-grained hardware pointer updates. Unlike the Read/Write Shared
Memory endpoitn modules, which are period based, where the reported
pointer advances only at period boundaries, where as push-pull mode
allows .pointer() to reflect sub-period progress, improving pointer
accuracy.
Also the driver now can queue buffers which are less than period size,
which makes tests like alsa_conformance_test happy.

Now the pointer update visibility is around 1ms, compared to min of
10ms with read/write shared memory endpoints.

Along with the circular buffer support, this patchset also adds
watermark event support to provide a period level event from dsp to
notify about period progress.

Tested this on T14s, Arduino VENTUNO-Q platforms.

Tplg related changes are available at:
https://github.com/Srinivas-Kandagatla/audioreach-topology/tree/push/pull

Link: https://patch.msgid.link/20260528185806.6316-1-srinivas.kandagatla@oss.qualcomm.com
This commit is contained in:
Mark Brown 2026-06-01 17:19:17 +01:00
commit 3ef902c960
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
5 changed files with 467 additions and 91 deletions

View File

@ -955,7 +955,7 @@ int audioreach_compr_set_param(struct q6apm_graph *graph,
struct media_format *header;
int rc;
void *p;
int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
int iid = graph->shm_iid;
int payload_size = sizeof(struct apm_sh_module_media_fmt_cmd);
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(payload_size,
@ -1118,6 +1118,42 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
int audioreach_shmem_register_event(struct q6apm_graph *graph, int bytes, int num_levels)
{
struct apm_module_register_events *event;
struct event_cfg_sh_mem_pull_push_mode_watermark_t *level;
int i, payload_size;
struct gpr_pkt *pkt __free(kfree) = NULL;
void *p;
if (num_levels <= 0 || bytes <= 0)
return -EINVAL;
payload_size = sizeof(*event) + sizeof(*level) + num_levels * sizeof(uint32_t);
pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_REGISTER_MODULE_EVENTS, 0,
graph->port->id, graph->shm_iid);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
event = p;
event->module_instance_id = graph->shm_iid;
event->event_id = EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK;
event->is_register = 1;
event->event_config_payload_size = sizeof(*level) + num_levels * sizeof(uint32_t);
p += sizeof(*event);
level = p;
level->num_water_mark_levels = num_levels;
for (i = 0; i < num_levels; i++)
level->level[i] = (i + 1) * bytes;
return audioreach_graph_send_cmd_sync(graph, pkt, 0);
}
EXPORT_SYMBOL_GPL(audioreach_shmem_register_event);
static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
const struct audioreach_module *module,
const struct audioreach_module_config *mcfg)
@ -1342,6 +1378,7 @@ int audioreach_set_media_format(struct q6apm_graph *graph,
rc = audioreach_i2s_set_media_format(graph, module, cfg);
break;
case MODULE_ID_WR_SHARED_MEM_EP:
case MODULE_ID_SH_MEM_PULL_MODE:
rc = audioreach_shmem_set_media_format(graph, module, cfg);
break;
case MODULE_ID_GAIN:
@ -1401,10 +1438,48 @@ void audioreach_graph_free_buf(struct q6apm_graph *graph)
}
EXPORT_SYMBOL_GPL(audioreach_graph_free_buf);
int audioreach_setup_push_pull(struct q6apm_graph *graph, phys_addr_t bphys,
phys_addr_t pphys, uint32_t mem_map_handle,
uint32_t pos_buf_mem_map_handle, uint32_t size)
{
struct param_id_sh_mem_pull_push_mode_cfg *cfg;
struct apm_module_param_data *param_data;
int payload_size;
struct gpr_pkt *pkt __free(kfree) = NULL;
void *p;
payload_size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE;
pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = p;
param_data->module_instance_id = graph->shm_iid;
param_data->error_code = 0;
param_data->param_id = PARAM_ID_SH_MEM_PULL_PUSH_MODE_CFG;
param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
p = p + APM_MODULE_PARAM_DATA_SIZE;
cfg = p;
cfg->shared_circ_buf_addr_lsw = lower_32_bits(bphys);
cfg->shared_circ_buf_addr_msw = upper_32_bits(bphys);
cfg->shared_circ_buf_size = size;
cfg->circ_buf_mem_map_handle = mem_map_handle;
cfg->shared_pos_buf_addr_lsw = lower_32_bits(pphys);
cfg->shared_pos_buf_addr_msw = upper_32_bits(pphys);
cfg->pos_buf_mem_map_handle = pos_buf_mem_map_handle;
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
EXPORT_SYMBOL_GPL(audioreach_setup_push_pull);
int audioreach_shared_memory_send_eos(struct q6apm_graph *graph)
{
struct data_cmd_wr_sh_mem_ep_eos *eos;
int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
int iid = graph->shm_iid;
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(sizeof(*eos),
DATA_CMD_WR_SH_MEM_EP_EOS, 0, graph->port->id, iid);
if (IS_ERR(pkt))

View File

@ -16,6 +16,8 @@ struct q6apm_graph;
#define MODULE_ID_PCM_CNV 0x07001003
#define MODULE_ID_PCM_ENC 0x07001004
#define MODULE_ID_PCM_DEC 0x07001005
#define MODULE_ID_SH_MEM_PULL_MODE 0x07001006
#define MODULE_ID_SH_MEM_PUSH_MODE 0x07001007
#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008
#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
#define MODULE_ID_I2S_SINK 0x0700100A
@ -60,10 +62,57 @@ struct q6apm_graph;
#define APM_CMD_GET_CFG 0x01001007
#define APM_CMD_SHARED_MEM_MAP_REGIONS 0x0100100C
#define APM_CMD_SHARED_MEM_UNMAP_REGIONS 0x0100100D
#define APM_CMD_REGISTER_MODULE_EVENTS 0x0100100E
#define APM_EVENT_MODULE_TO_CLIENT 0x03001000
#define APM_CMD_RSP_SHARED_MEM_MAP_REGIONS 0x02001001
#define APM_MMAP_TOKEN_GID_MASK GENMASK(15, 0)
#define APM_MMAP_TOKEN_MAP_TYPE_POS_BUF BIT(16)
#define APM_MMAP_TOKEN_MAP_TYPE_SHIFT 16
#define APM_CMD_RSP_GET_CFG 0x02001000
#define APM_CMD_CLOSE_ALL 0x01001013
#define APM_CMD_REGISTER_SHARED_CFG 0x0100100A
#define EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK 0x0800101C
/**
* struct event_cfg_sh_mem_pull_push_mode_watermark_t - Watermark config
* @num_water_mark_levels: Number of watermark levels.
* @level: Watermark levels.
*
* If @num_water_mark_levels is zero, no watermark levels are specified
* and watermark events are not supported.
*/
struct event_cfg_sh_mem_pull_push_mode_watermark_t {
uint32_t num_water_mark_levels;
uint32_t level[];
} __packed;
/**
* struct apm_module_register_events - Register or unregister module events
* @module_instance_id: Module instance identifier.
* @event_id: Module event identifier.
* @is_register: 1 to register the event, 0 to unregister it.
* @error_code: Error code for out-of-band command mode.
* @event_config_payload_size: Event configuration payload size in bytes.
* @reserved: Reserved for alignment; must be zero.
*/
struct apm_module_register_events {
uint32_t module_instance_id;
uint32_t event_id;
uint32_t is_register;
uint32_t error_code;
uint32_t event_config_payload_size;
uint32_t reserved;
} __packed;
/**
* struct apm_module_event - Module event descriptor
* @event_id: Module event identifier.
* @event_payload_size: Event payload size in bytes.
*/
struct apm_module_event {
uint32_t event_id;
uint32_t event_payload_size;
} __packed;
#define APM_MEMORY_MAP_SHMEM8_4K_POOL 3
@ -710,6 +759,46 @@ struct param_id_placeholder_real_module_id {
uint32_t real_module_id;
} __packed;
#define PARAM_ID_SH_MEM_PULL_PUSH_MODE_CFG 0x0800100A
/**
* struct param_id_sh_mem_pull_push_mode_cfg - Shared memory push/pull config
* @shared_circ_buf_addr_lsw: Lower 32 bits of the circular buffer address.
* @shared_circ_buf_addr_msw: Upper 32 bits of the circular buffer address.
* @shared_circ_buf_size: Circular buffer size in bytes.
* @circ_buf_mem_map_handle: Circular buffer memory map handle.
* @shared_pos_buf_addr_lsw: Lower 32 bits of the position buffer address.
* @shared_pos_buf_addr_msw: Upper 32 bits of the position buffer address.
* @pos_buf_mem_map_handle: Position buffer memory map handle.
*/
struct param_id_sh_mem_pull_push_mode_cfg {
uint32_t shared_circ_buf_addr_lsw;
uint32_t shared_circ_buf_addr_msw;
uint32_t shared_circ_buf_size;
uint32_t circ_buf_mem_map_handle;
uint32_t shared_pos_buf_addr_lsw;
uint32_t shared_pos_buf_addr_msw;
uint32_t pos_buf_mem_map_handle;
} __packed;
/**
* struct sh_mem_pull_push_mode_position_buffer - Shared position buffer
* @frame_counter: Synchronization counter.
* @index: Current read/write index in bytes.
* @timestamp_us_lsw: Lower 32 bits of the timestamp in microseconds.
* @timestamp_us_msw: Upper 32 bits of the timestamp in microseconds.
*
* The frame counter should be read before and after the other fields to
* ensure the DSP did not update them while they were being read.
*/
struct sh_mem_pull_push_mode_position_buffer {
uint32_t frame_counter;
uint32_t index;
uint32_t timestamp_us_lsw;
uint32_t timestamp_us_msw;
} __packed;
/* Graph */
struct audioreach_connection {
/* Connections */
@ -723,8 +812,10 @@ struct audioreach_connection {
struct audioreach_graph_info {
int id;
uint32_t mem_map_handle;
uint32_t pos_buf_mem_map_handle;
uint32_t num_sub_graphs;
struct list_head sg_list;
bool is_push_pull_mode;
/* DPCM connection from FE Graph to BE graph */
uint32_t src_mod_inst_id;
uint32_t src_mod_op_port_id;
@ -855,5 +946,10 @@ int audioreach_send_u32_param(struct q6apm_graph *graph,
uint32_t param_id, uint32_t param_val);
int audioreach_compr_set_param(struct q6apm_graph *graph,
const struct audioreach_module_config *mcfg);
int audioreach_setup_push_pull(struct q6apm_graph *graph, phys_addr_t bphys,
phys_addr_t pphys, uint32_t mem_map_handle,
uint32_t pos_buf_mem_map_handle, uint32_t size);
int audioreach_map_memory_position_buffer(struct q6apm_graph *graph, unsigned int dir);
int audioreach_shmem_register_event(struct q6apm_graph *graph, int bytes, int num_levels);
#endif /* __AUDIOREACH_H__ */

View File

@ -18,6 +18,7 @@
#include "q6apm.h"
#define DRV_NAME "q6apm-dai"
#define POS_BUFFER_BYTES 4096
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
@ -62,8 +63,12 @@ struct q6apm_dai_rtd {
struct snd_codec codec;
struct snd_compr_params codec_param;
struct snd_dma_buffer dma_buffer;
struct sh_mem_pull_push_mode_position_buffer *pos_buffer;
uint32_t last_pos_index;
phys_addr_t phys;
phys_addr_t pos_phys;
unsigned int pcm_size;
unsigned int push_pull_size;
unsigned int pcm_count;
unsigned int periods;
uint64_t bytes_sent;
@ -128,6 +133,9 @@ static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *
struct snd_pcm_substream *substream = prtd->substream;
switch (opcode) {
case APM_CLIENT_EVENT_WATERMARK_EVENT:
snd_pcm_period_elapsed(substream);
break;
case APM_CLIENT_EVENT_CMD_EOS_DONE:
prtd->state = Q6APM_STREAM_STOPPED;
break;
@ -234,24 +242,47 @@ static int q6apm_dai_prepare(struct snd_soc_component *component,
q6apm_free_fragments(prtd->graph, substream->stream);
}
prtd->last_pos_index = 0;
prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
/* rate and channels are sent to audio driver */
ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
if (ret < 0) {
dev_err(dev, "%s: q6apm_open_write failed\n", __func__);
return ret;
if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) {
if (prtd->pcm_size != prtd->push_pull_size) {
ret = q6apm_push_pull_config(prtd->graph, prtd->phys, prtd->pos_phys,
prtd->pcm_size);
if (ret < 0) {
dev_err(dev, "Push/Pull config failed rc = %d\n", ret);
return ret;
}
ret = q6apm_register_watermark_event(prtd->graph,
prtd->pcm_size / prtd->periods,
prtd->periods);
if (ret < 0) {
dev_err(dev, "WaterMark event config failed rc = %d\n", ret);
return ret;
}
prtd->push_pull_size = prtd->pcm_size;
}
} else {
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 ret;
}
}
ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
if (ret < 0)
dev_err(dev, "%s: CMD Format block failed\n", __func__);
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;
dev_err(dev, "%s: CMD Format block failed\n", __func__);
return ret;
}
/* rate and channels are sent to audio driver */
ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
if (ret < 0) {
dev_err(dev, "Failed to set media format %d\n", ret);
return ret;
}
ret = q6apm_graph_prepare(prtd->graph);
@ -265,13 +296,13 @@ static int q6apm_dai_prepare(struct snd_soc_component *component,
dev_err(dev, "Failed to Start Graph %d\n", ret);
return ret;
}
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
int i;
/* Queue the buffers for Capture ONLY after graph is started */
for (i = 0; i < runtime->periods; i++)
q6apm_read(prtd->graph);
if (!q6apm_is_graph_in_push_pull_mode(prtd->graph)) {
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
int i;
/* Queue the buffers for Capture ONLY after graph is started */
for (i = 0; i < runtime->periods; i++)
q6apm_read(prtd->graph);
}
}
/* Now that graph as been prepared and started update the internal state accordingly */
@ -286,6 +317,9 @@ static int q6apm_dai_ack(struct snd_soc_component *component, struct snd_pcm_sub
struct q6apm_dai_rtd *prtd = runtime->private_data;
int i, ret = 0, avail_periods;
if (q6apm_is_graph_in_push_pull_mode(prtd->graph))
return 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
avail_periods = (runtime->control->appl_ptr - prtd->queue_ptr)/runtime->period_size;
for (i = 0; i < avail_periods; i++) {
@ -317,6 +351,7 @@ static int q6apm_dai_trigger(struct snd_soc_component *component,
/* TODO support be handled via SoftPause Module */
prtd->state = Q6APM_STREAM_STOPPED;
prtd->queue_ptr = 0;
prtd->last_pos_index = 0;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@ -402,6 +437,14 @@ static int q6apm_dai_open(struct snd_soc_component *component,
else
prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) {
void *pos_buffer;
prtd->pos_phys = prtd->phys + BUFFER_BYTES_MAX;
pos_buffer = (void *)(substream->dma_buffer.area + BUFFER_BYTES_MAX);
prtd->pos_buffer = (struct sh_mem_pull_push_mode_position_buffer *)(pos_buffer);
}
return 0;
err:
kfree(prtd);
@ -436,6 +479,25 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
struct q6apm_dai_rtd *prtd = runtime->private_data;
snd_pcm_uframes_t ptr;
if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) {
int retries = 10;
uint32_t index, fc1, fc2;
/* index is valid if frame_counter does not change while reading. */
do {
fc1 = READ_ONCE(prtd->pos_buffer->frame_counter);
index = READ_ONCE(prtd->pos_buffer->index);
fc2 = READ_ONCE(prtd->pos_buffer->frame_counter);
} while (fc1 != fc2 && --retries);
if (fc1 != fc2)
index = prtd->last_pos_index;
else
prtd->last_pos_index = index;
ptr = bytes_to_frames(runtime, index);
return ptr;
}
ptr = q6apm_get_hw_pointer(prtd->graph, substream->stream) * runtime->period_size;
if (ptr)
return ptr - 1;
@ -468,7 +530,8 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component,
}
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, bool is_push_pull)
{
struct q6apm_dai_data *pdata;
struct device *dev = component->dev;
@ -490,6 +553,19 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component,
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;
else
phys = (substream->dma_buffer.addr + BUFFER_BYTES_MAX) | (pdata->sid << 32);
ret = q6apm_map_pos_buffer(dev, graph_id, phys, POS_BUFFER_BYTES);
if (ret < 0)
dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret);
} else {
}
return ret;
}
@ -504,25 +580,30 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc
*/
int size = BUFFER_BYTES_MAX + PAGE_SIZE;
int graph_id, ret;
struct snd_pcm_substream *substream;
bool is_push_pull;
struct snd_pcm_substream *substream = NULL;
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) {
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);
else if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
if (substream) {
is_push_pull = q6apm_is_graph_in_push_pull_mode_from_id(component->dev,
graph_id,
substream->stream);
if (is_push_pull)
size += POS_BUFFER_BYTES;
ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
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, is_push_pull);
if (ret)
return ret;
}
@ -547,6 +628,9 @@ static void q6apm_dai_memory_unmap(struct snd_soc_component *component,
graph_id = cpu_dai->driver->id;
q6apm_unmap_memory_fixed_region(component->dev, graph_id);
if (q6apm_is_graph_in_push_pull_mode_from_id(component->dev, graph_id, substream->stream))
q6apm_unmap_pos_buffer(component->dev, graph_id);
}
static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm)

View File

@ -186,23 +186,27 @@ int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
{
struct audioreach_module *module;
if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
else
module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE) {
module = q6apm_find_module_by_mid(graph, MODULE_ID_SH_MEM_PUSH_MODE);
if (!module)
module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
} else {
module = q6apm_find_module_by_mid(graph, MODULE_ID_SH_MEM_PULL_MODE);
if (!module)
module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
}
if (!module)
if (!module) {
dev_err(graph->dev, "No SHMEM module found in graph\n");
return -ENODEV;
}
audioreach_set_media_format(graph, module, cfg);
return 0;
return audioreach_set_media_format(graph, module, cfg);
}
EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phys_addr_t phys,
size_t sz)
static int __q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id,
phys_addr_t phys, size_t sz, bool is_pos_buf)
{
struct audioreach_graph_info *info;
struct q6apm *apm = dev_get_drvdata(dev->parent);
@ -211,8 +215,10 @@ int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phy
int payload_size = sizeof(*cmd) + (sizeof(*mregions));
uint32_t buf_sz;
void *p;
uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0;
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size,
APM_CMD_SHARED_MEM_MAP_REGIONS, graph_id);
APM_CMD_SHARED_MEM_MAP_REGIONS, (graph_id | pos_mask));
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@ -220,8 +226,13 @@ int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phy
if (!info)
return -ENODEV;
if (info->mem_map_handle)
return 0;
if (is_pos_buf) {
if (info->pos_buf_mem_map_handle)
return 0;
} else {
if (info->mem_map_handle)
return 0;
}
/* DSP expects size should be aligned to 4K */
buf_sz = ALIGN(sz, 4096);
@ -230,7 +241,10 @@ int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phy
cmd = p;
cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL;
cmd->num_regions = 1;
cmd->property_flag = 0x0;
if (is_pos_buf)
cmd->property_flag = 0x2;
else
cmd->property_flag = 0x0;
mregions = p + sizeof(*cmd);
@ -240,6 +254,18 @@ int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id, phy
return q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS);
}
int q6apm_map_pos_buffer(struct device *dev, unsigned int graph_id, phys_addr_t phys, size_t sz)
{
return __q6apm_map_memory_fixed_region(dev, graph_id, phys, sz, true);
}
EXPORT_SYMBOL_GPL(q6apm_map_pos_buffer);
int q6apm_map_memory_fixed_region(struct device *dev, unsigned int graph_id,
phys_addr_t phys, size_t sz)
{
return __q6apm_map_memory_fixed_region(dev, graph_id, phys, sz, false);
}
EXPORT_SYMBOL_GPL(q6apm_map_memory_fixed_region);
int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
@ -293,11 +319,13 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr
}
EXPORT_SYMBOL_GPL(q6apm_alloc_fragments);
int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id)
static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id,
bool is_pos_buf)
{
struct apm_cmd_shared_mem_unmap_regions *cmd;
struct q6apm *apm = dev_get_drvdata(dev->parent);
struct audioreach_graph_info *info;
uint32_t mem_map_handle;
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))
@ -307,16 +335,35 @@ int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id)
if (!info)
return -ENODEV;
if (!info->mem_map_handle)
return 0;
if (is_pos_buf) {
if (!info->pos_buf_mem_map_handle)
return 0;
mem_map_handle = info->pos_buf_mem_map_handle;
} else {
if (!info->mem_map_handle)
return 0;
mem_map_handle = info->mem_map_handle;
}
cmd = (void *)pkt + GPR_HDR_SIZE;
cmd->mem_map_handle = info->mem_map_handle;
cmd->mem_map_handle = mem_map_handle;
return q6apm_send_cmd_sync(apm, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
}
int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id)
{
return __q6apm_unmap_memory_fixed_region(dev, graph_id, false);
}
EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region);
int q6apm_unmap_pos_buffer(struct device *dev, unsigned int graph_id)
{
return __q6apm_unmap_memory_fixed_region(dev, graph_id, true);
}
EXPORT_SYMBOL_GPL(q6apm_unmap_pos_buffer);
int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir)
{
audioreach_graph_free_buf(graph);
@ -399,15 +446,20 @@ int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_mo
struct audioreach_sub_graph *sgs;
struct audioreach_container *container;
struct audioreach_module *module;
int ret;
list_for_each_entry(sgs, &info->sg_list, node) {
list_for_each_entry(container, &sgs->container_list, node) {
list_for_each_entry(module, &container->modules_list, node) {
if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||
(module->module_id == MODULE_ID_RD_SHARED_MEM_EP))
(module->module_id == MODULE_ID_RD_SHARED_MEM_EP) ||
(module->module_id == MODULE_ID_SH_MEM_PULL_MODE) ||
(module->module_id == MODULE_ID_SH_MEM_PUSH_MODE))
continue;
audioreach_set_media_format(graph, module, cfg);
ret = audioreach_set_media_format(graph, module, cfg);
if (ret)
return ret;
}
}
}
@ -417,31 +469,6 @@ int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_mo
}
EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
{
struct audioreach_module *module;
module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
if (!module)
return -ENODEV;
return module->instance_id;
}
int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
{
struct audioreach_module *module;
module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
if (!module)
return -ENODEV;
return module->instance_id;
}
EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
uint32_t lsw_ts, uint32_t wflags)
{
@ -530,6 +557,7 @@ static int graph_callback(const 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_module_event *event;
const struct gpr_ibasic_rsp_result_t *result;
struct q6apm_graph *graph = priv;
const struct gpr_hdr *hdr = &data->hdr;
@ -541,6 +569,16 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
result = data->payload;
switch (hdr->opcode) {
case APM_EVENT_MODULE_TO_CLIENT:
event = data->payload;
switch (event->event_id) {
case EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK:
client_event = APM_CLIENT_EVENT_WATERMARK_EVENT;
graph->cb(client_event, hdr->token, data->payload, graph->priv);
break;
}
break;
case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
if (!graph->ar_graph)
break;
@ -596,6 +634,7 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
switch (result->opcode) {
case APM_CMD_SHARED_MEM_MAP_REGIONS:
case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
case APM_CMD_REGISTER_MODULE_EVENTS:
case APM_CMD_SET_CFG:
graph->result.opcode = result->opcode;
graph->result.status = result->status;
@ -614,13 +653,67 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
return 0;
}
int q6apm_register_watermark_event(struct q6apm_graph *graph, int water_mark_level_bytes,
int num_levels)
{
return audioreach_shmem_register_event(graph, water_mark_level_bytes, num_levels);
}
EXPORT_SYMBOL_GPL(q6apm_register_watermark_event);
int q6apm_push_pull_config(struct q6apm_graph *graph, phys_addr_t bphys,
phys_addr_t pphys, uint32_t size)
{
struct audioreach_graph_info *info = graph->info;
return audioreach_setup_push_pull(graph, bphys, pphys, info->mem_map_handle,
info->pos_buf_mem_map_handle, size);
}
EXPORT_SYMBOL_GPL(q6apm_push_pull_config);
bool q6apm_is_graph_in_push_pull_mode_from_id(struct device *dev, unsigned int graph_id, int dir)
{
struct audioreach_graph_info *info;
struct q6apm *apm = dev_get_drvdata(dev->parent);
struct audioreach_module *module;
info = idr_find(&apm->graph_info_idr, graph_id);
if (!info)
return false;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
module = __q6apm_find_module_by_mid(apm, info, MODULE_ID_SH_MEM_PULL_MODE);
else
module = __q6apm_find_module_by_mid(apm, info, MODULE_ID_SH_MEM_PUSH_MODE);
return !!module;
}
EXPORT_SYMBOL_GPL(q6apm_is_graph_in_push_pull_mode_from_id);
bool q6apm_is_graph_in_push_pull_mode(struct q6apm_graph *graph)
{
return graph->info->is_push_pull_mode;
}
EXPORT_SYMBOL_GPL(q6apm_is_graph_in_push_pull_mode);
static int q6apm_graph_get_module_iid(struct q6apm_graph *graph, uint32_t mid)
{
struct audioreach_module *module;
module = q6apm_find_module_by_mid(graph, mid);
if (!module)
return -ENODEV;
return module->instance_id;
}
struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
void *priv, int graph_id, int dir)
{
struct q6apm *apm = dev_get_drvdata(dev->parent);
struct audioreach_graph *ar_graph;
struct q6apm_graph *graph;
int ret;
int ret, iid = 0;
ar_graph = q6apm_get_audioreach_graph(apm, graph_id);
if (IS_ERR(ar_graph)) {
@ -642,11 +735,23 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
graph->id = ar_graph->id;
graph->dev = dev;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
graph->shm_iid = q6apm_graph_get_rx_shmem_module_iid(graph);
else
graph->shm_iid = q6apm_graph_get_tx_shmem_module_iid(graph);
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
iid = q6apm_graph_get_module_iid(graph, MODULE_ID_SH_MEM_PULL_MODE);
if (iid < 0)
iid = q6apm_graph_get_module_iid(graph, MODULE_ID_WR_SHARED_MEM_EP);
else
graph->info->is_push_pull_mode = true;
} else {
iid = q6apm_graph_get_module_iid(graph, MODULE_ID_SH_MEM_PUSH_MODE);
if (iid < 0)
iid = q6apm_graph_get_module_iid(graph, MODULE_ID_RD_SHARED_MEM_EP);
else
graph->info->is_push_pull_mode = true;
}
if (iid > 0)
graph->shm_iid = iid;
mutex_init(&graph->lock);
init_waitqueue_head(&graph->cmd_wait);
@ -803,6 +908,7 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
struct device *dev = &gdev->dev;
struct gpr_ibasic_rsp_result_t *result;
const struct gpr_hdr *hdr = &data->hdr;
int graph_id, is_pos_buf;
result = data->payload;
@ -853,13 +959,19 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
apm->result.opcode = hdr->opcode;
apm->result.status = 0;
rsp = data->payload;
graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK;
is_pos_buf = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF;
info = idr_find(&apm->graph_info_idr, hdr->token);
if (info)
info->mem_map_handle = rsp->mem_map_handle;
else
info = idr_find(&apm->graph_info_idr, graph_id);
if (info) {
if (is_pos_buf)
info->pos_buf_mem_map_handle = rsp->mem_map_handle;
else
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;

View File

@ -41,6 +41,7 @@
#define APM_CLIENT_EVENT_CMD_RUN_DONE 0x1008
#define APM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009
#define APM_CLIENT_EVENT_DATA_READ_DONE 0x100a
#define APM_CLIENT_EVENT_WATERMARK_EVENT 0x100b
#define APM_WRITE_TOKEN_MASK GENMASK(15, 0)
#define APM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16)
#define APM_WRITE_TOKEN_LEN_SHIFT 16
@ -136,6 +137,10 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
int q6apm_map_memory_fixed_region(struct device *dev,
unsigned int graph_id, phys_addr_t phys,
size_t sz);
int q6apm_map_pos_buffer(struct device *dev,
unsigned int graph_id, phys_addr_t phys,
size_t sz);
int q6apm_unmap_pos_buffer(struct device *dev, unsigned int graph_id);
int q6apm_alloc_fragments(struct q6apm_graph *graph,
unsigned int dir, phys_addr_t phys,
size_t period_sz, unsigned int periods);
@ -148,8 +153,6 @@ int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt,
/* Callback for graph specific */
struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph,
uint32_t mid);
int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
bool q6apm_is_adsp_ready(void);
int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en);
@ -157,4 +160,10 @@ int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph,
int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples);
int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id);
int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir);
bool q6apm_is_graph_in_push_pull_mode(struct q6apm_graph *graph);
bool q6apm_is_graph_in_push_pull_mode_from_id(struct device *dev, unsigned int graph_id, int dir);
int q6apm_push_pull_config(struct q6apm_graph *graph, phys_addr_t bphys,
phys_addr_t pphys, uint32_t size);
int q6apm_register_watermark_event(struct q6apm_graph *graph, int watermark_bytes, int num_levels);
#endif /* __APM_GRAPH_ */