diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index a13f753eff98..e6e9eb2e85aa 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -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)) diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 6859770b38a6..62a2fd79bbcb 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -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__ */ diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 3a1be41df096..bf1f872a09f4 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -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) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2ab378fb5032..2e5b25b8d00f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -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; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 376a36700c53..5cb51ca491dc 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -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_ */