diff options
Diffstat (limited to 'sound/soc/qcom/qdsp6/q6apm.c')
| -rw-r--r-- | sound/soc/qcom/qdsp6/q6apm.c | 256 |
1 files changed, 130 insertions, 126 deletions
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index f424d7aa389a..94cc6376a367 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -27,6 +27,8 @@ struct apm_graph_mgmt_cmd { #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8) +static struct q6apm *g_apm; + int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode) { gpr_device_t *gdev = apm->gdev; @@ -63,7 +65,7 @@ static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, ui graph->info = info; graph->id = graph_id; - graph->graph = audioreach_alloc_graph_pkt(apm, &info->sg_list, graph_id); + graph->graph = audioreach_alloc_graph_pkt(apm, info); if (IS_ERR(graph->graph)) { void *err = graph->graph; @@ -75,6 +77,7 @@ static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, ui id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL); if (id < 0) { dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id); + kfree(graph->graph); kfree(graph); mutex_unlock(&apm->lock); return ERR_PTR(id); @@ -96,12 +99,8 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op struct apm_graph_mgmt_cmd *mgmt_cmd; struct audioreach_sub_graph *sg; struct q6apm *apm = graph->apm; - int i = 0, rc, payload_size; - struct gpr_pkt *pkt; - - payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs); - - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0); + int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -117,11 +116,7 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op list_for_each_entry(sg, &info->sg_list, node) mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id; - rc = q6apm_send_cmd_sync(apm, pkt, 0); - - kfree(pkt); - - return rc; + return q6apm_send_cmd_sync(apm, pkt, 0); } static void q6apm_put_audioreach_graph(struct kref *ref) @@ -142,21 +137,28 @@ static void q6apm_put_audioreach_graph(struct kref *ref) kfree(graph); } + static int q6apm_get_apm_state(struct q6apm *apm) { - struct gpr_pkt *pkt; - - pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(0, + APM_CMD_GET_SPF_STATE, 0); if (IS_ERR(pkt)) return PTR_ERR(pkt); q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE); - kfree(pkt); - return apm->state; } +bool q6apm_is_adsp_ready(void) +{ + if (g_apm) + return q6apm_get_apm_state(g_apm); + + return false; +} +EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready); + static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm, struct audioreach_graph_info *info, uint32_t mid) @@ -177,87 +179,6 @@ static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm, return NULL; } -static struct audioreach_module *q6apm_graph_get_last_module(struct q6apm *apm, u32 sgid) -{ - struct audioreach_container *container; - struct audioreach_module *module; - struct audioreach_sub_graph *sg; - - mutex_lock(&apm->lock); - sg = idr_find(&apm->sub_graphs_idr, sgid); - mutex_unlock(&apm->lock); - if (!sg) - return NULL; - - container = list_last_entry(&sg->container_list, struct audioreach_container, node); - module = audioreach_get_container_last_module(container); - - return module; -} - -static struct audioreach_module *q6apm_graph_get_first_module(struct q6apm *apm, u32 sgid) -{ - struct audioreach_container *container; - struct audioreach_module *module; - struct audioreach_sub_graph *sg; - - mutex_lock(&apm->lock); - sg = idr_find(&apm->sub_graphs_idr, sgid); - mutex_unlock(&apm->lock); - if (!sg) - return NULL; - - container = list_first_entry(&sg->container_list, struct audioreach_container, node); - module = audioreach_get_container_first_module(container); - - return module; -} - -bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid, u32 dst_sgid) -{ - struct audioreach_module *module; - u32 iid; - - module = q6apm_graph_get_last_module(apm, src_sgid); - if (!module) - return false; - - iid = module->instance_id; - module = q6apm_graph_get_first_module(apm, dst_sgid); - if (!module) - return false; - - if (module->src_mod_inst_id == iid) - return true; - - return false; -} - -int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid, bool connect) -{ - struct audioreach_module *module; - u32 iid; - - if (connect) { - module = q6apm_graph_get_last_module(apm, src_sgid); - if (!module) - return -ENODEV; - - iid = module->instance_id; - } else { - iid = 0; - } - - module = q6apm_graph_get_first_module(apm, dst_sgid); - if (!module) - return -ENODEV; - - /* set src module in dst subgraph first module */ - module->src_mod_inst_id = iid; - - return 0; -} - int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { @@ -298,7 +219,7 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a return 0; } - buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL); + buf = kcalloc(periods, sizeof(struct audio_buffer), GFP_KERNEL); if (!buf) { mutex_unlock(&graph->lock); return -ENOMEM; @@ -338,7 +259,7 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) { struct apm_cmd_shared_mem_unmap_regions *cmd; struct audioreach_graph_data *data; - struct gpr_pkt *pkt; + struct gpr_pkt *pkt __free(kfree) = NULL; int rc; if (dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -358,7 +279,6 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) cmd->mem_map_handle = data->mem_map_handle; rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS); - kfree(pkt); audioreach_graph_free_buf(graph); @@ -366,6 +286,74 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) } EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions); +int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) +{ + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples); +} +EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence); + +int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) +{ + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples); +} +EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence); + +int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en) +{ + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en); +} +EXPORT_SYMBOL_GPL(q6apm_enable_compress_module); + +int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, + uint32_t codec_id) +{ + struct audioreach_module *module; + uint32_t module_id; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + switch (codec_id) { + case SND_AUDIOCODEC_MP3: + module_id = MODULE_ID_MP3_DECODE; + break; + case SND_AUDIOCODEC_AAC: + module_id = MODULE_ID_AAC_DEC; + break; + case SND_AUDIOCODEC_FLAC: + module_id = MODULE_ID_FLAC_DEC; + break; + case SND_AUDIOCODEC_OPUS_RAW: + module_id = MODULE_ID_OPUS_DEC; + break; + default: + return -EINVAL; + } + + return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID, + module_id); +} +EXPORT_SYMBOL_GPL(q6apm_set_real_module_id); + int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { struct audioreach_graph_info *info = graph->info; @@ -420,13 +408,11 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, { struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer; struct audio_buffer *ab; - struct gpr_pkt *pkt; - int rc, iid; - - iid = q6apm_graph_get_rx_shmem_module_iid(graph); - pkt = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, - graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), - graph->port->id, iid); + int iid = q6apm_graph_get_rx_shmem_module_iid(graph); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer), + DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, + graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), + graph->port->id, iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -450,11 +436,7 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, mutex_unlock(&graph->lock); - rc = gpr_send_port_pkt(graph->port, pkt); - - kfree(pkt); - - return rc; + return gpr_send_port_pkt(graph->port, pkt); } EXPORT_SYMBOL_GPL(q6apm_write_async); @@ -463,12 +445,10 @@ int q6apm_read(struct q6apm_graph *graph) struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer; struct audioreach_graph_data *port; struct audio_buffer *ab; - struct gpr_pkt *pkt; - int rc, iid; - - iid = q6apm_graph_get_tx_shmem_module_iid(graph); - pkt = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, - graph->tx_data.dsp_buf, graph->port->id, iid); + int iid = q6apm_graph_get_tx_shmem_module_iid(graph); + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer), + DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, + graph->tx_data.dsp_buf, graph->port->id, iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -490,13 +470,23 @@ int q6apm_read(struct q6apm_graph *graph) mutex_unlock(&graph->lock); - rc = gpr_send_port_pkt(graph->port, pkt); - kfree(pkt); - - return rc; + return gpr_send_port_pkt(graph->port, pkt); } EXPORT_SYMBOL_GPL(q6apm_read); +int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir) +{ + struct audioreach_graph_data *data; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + data = &graph->rx_data; + else + data = &graph->tx_data; + + return (int)atomic_read(&data->hw_ptr); +} +EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer); + static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) { struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; @@ -514,6 +504,8 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) switch (hdr->opcode) { case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2: + if (!graph->ar_graph) + break; client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE; mutex_lock(&graph->lock); token = hdr->token & APM_WRITE_TOKEN_MASK; @@ -521,7 +513,8 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) done = data->payload; phys = graph->rx_data.buf[token].phys; mutex_unlock(&graph->lock); - + /* token numbering starts at 0 */ + atomic_set(&graph->rx_data.hw_ptr, token + 1); if (lower_32_bits(phys) == done->buf_addr_lsw && upper_32_bits(phys) == done->buf_addr_msw) { graph->result.opcode = hdr->opcode; @@ -547,11 +540,15 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) wake_up(&graph->cmd_wait); break; case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2: + if (!graph->ar_graph) + break; client_event = APM_CLIENT_EVENT_DATA_READ_DONE; mutex_lock(&graph->lock); rd_done = data->payload; phys = graph->tx_data.buf[hdr->token].phys; mutex_unlock(&graph->lock); + /* token numbering starts at 0 */ + atomic_set(&graph->tx_data.hw_ptr, hdr->token + 1); if (upper_32_bits(phys) == rd_done->buf_addr_msw && lower_32_bits(phys) == rd_done->buf_addr_lsw) { @@ -565,6 +562,9 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) } break; case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED: + client_event = APM_CLIENT_EVENT_CMD_EOS_DONE; + if (graph->cb) + graph->cb(client_event, hdr->token, data->payload, graph->priv); break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { @@ -649,8 +649,9 @@ int q6apm_graph_close(struct q6apm_graph *graph) { struct audioreach_graph *ar_graph = graph->ar_graph; - gpr_free_port(graph->port); + graph->ar_graph = NULL; kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph); + gpr_free_port(graph->port); kfree(graph); return 0; @@ -730,6 +731,7 @@ static int apm_probe(gpr_device_t *gdev) apm->gdev = gdev; init_waitqueue_head(&apm->wait); + INIT_LIST_HEAD(&apm->widget_list); idr_init(&apm->graph_idr); idr_init(&apm->graph_info_idr); idr_init(&apm->sub_graphs_idr); @@ -737,11 +739,13 @@ static int apm_probe(gpr_device_t *gdev) idr_init(&apm->modules_idr); + g_apm = apm; + q6apm_get_apm_state(apm); ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); if (ret < 0) { - dev_err(dev, "failed to get register q6apm: %d\n", ret); + dev_err(dev, "failed to register q6apm: %d\n", ret); return ret; } |
