1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020, Linaro Limited
4 #include <dt-bindings/soc/qcom,gpr.h>
5 #include <linux/delay.h>
6 #include <linux/jiffies.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
10 #include <linux/of_platform.h>
11 #include <linux/sched.h>
12 #include <linux/slab.h>
13 #include <linux/soc/qcom/apr.h>
14 #include <linux/wait.h>
15 #include <sound/soc.h>
16 #include <sound/soc-dapm.h>
17 #include <sound/pcm.h>
18 #include "audioreach.h"
21 /* Graph Management */
22 struct apm_graph_mgmt_cmd {
23 struct apm_module_param_data param_data;
24 uint32_t num_sub_graphs;
25 uint32_t sub_graph_id_list[];
28 #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
30 int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
32 gpr_device_t *gdev = apm->gdev;
34 return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
35 NULL, &apm->wait, pkt, rsp_opcode);
38 static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, uint32_t graph_id)
40 struct audioreach_graph_info *info;
41 struct audioreach_graph *graph;
44 mutex_lock(&apm->lock);
45 graph = idr_find(&apm->graph_idr, graph_id);
46 mutex_unlock(&apm->lock);
49 kref_get(&graph->refcount);
53 info = idr_find(&apm->graph_info_idr, graph_id);
56 return ERR_PTR(-ENODEV);
58 graph = kzalloc(sizeof(*graph), GFP_KERNEL);
60 return ERR_PTR(-ENOMEM);
66 graph->graph = audioreach_alloc_graph_pkt(apm, &info->sg_list, graph_id);
67 if (IS_ERR(graph->graph)) {
68 void *err = graph->graph;
74 mutex_lock(&apm->lock);
75 id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL);
77 dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id);
80 mutex_unlock(&apm->lock);
83 mutex_unlock(&apm->lock);
85 kref_init(&graph->refcount);
87 q6apm_send_cmd_sync(apm, graph->graph, 0);
92 static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
94 struct audioreach_graph_info *info = graph->info;
95 int num_sub_graphs = info->num_sub_graphs;
96 struct apm_module_param_data *param_data;
97 struct apm_graph_mgmt_cmd *mgmt_cmd;
98 struct audioreach_sub_graph *sg;
99 struct q6apm *apm = graph->apm;
100 int i = 0, rc, payload_size;
103 payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
105 pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
109 mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
111 mgmt_cmd->num_sub_graphs = num_sub_graphs;
113 param_data = &mgmt_cmd->param_data;
114 param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
115 param_data->param_id = APM_PARAM_ID_SUB_GRAPH_LIST;
116 param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
118 list_for_each_entry(sg, &info->sg_list, node)
119 mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id;
121 rc = q6apm_send_cmd_sync(apm, pkt, 0);
128 static void q6apm_put_audioreach_graph(struct kref *ref)
130 struct audioreach_graph *graph;
133 graph = container_of(ref, struct audioreach_graph, refcount);
136 audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
138 mutex_lock(&apm->lock);
139 graph = idr_remove(&apm->graph_idr, graph->id);
140 mutex_unlock(&apm->lock);
146 static int q6apm_get_apm_state(struct q6apm *apm)
150 pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0);
154 q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
161 static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
162 struct audioreach_graph_info *info,
165 struct audioreach_container *container;
166 struct audioreach_sub_graph *sgs;
167 struct audioreach_module *module;
169 list_for_each_entry(sgs, &info->sg_list, node) {
170 list_for_each_entry(container, &sgs->container_list, node) {
171 list_for_each_entry(module, &container->modules_list, node) {
172 if (mid == module->module_id)
181 static struct audioreach_module *q6apm_graph_get_last_module(struct q6apm *apm, u32 sgid)
183 struct audioreach_container *container;
184 struct audioreach_module *module;
185 struct audioreach_sub_graph *sg;
187 mutex_lock(&apm->lock);
188 sg = idr_find(&apm->sub_graphs_idr, sgid);
189 mutex_unlock(&apm->lock);
193 container = list_last_entry(&sg->container_list, struct audioreach_container, node);
194 module = audioreach_get_container_last_module(container);
199 static struct audioreach_module *q6apm_graph_get_first_module(struct q6apm *apm, u32 sgid)
201 struct audioreach_container *container;
202 struct audioreach_module *module;
203 struct audioreach_sub_graph *sg;
205 mutex_lock(&apm->lock);
206 sg = idr_find(&apm->sub_graphs_idr, sgid);
207 mutex_unlock(&apm->lock);
211 container = list_first_entry(&sg->container_list, struct audioreach_container, node);
212 module = audioreach_get_container_first_module(container);
217 bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid, u32 dst_sgid)
219 struct audioreach_module *module;
222 module = q6apm_graph_get_last_module(apm, src_sgid);
226 iid = module->instance_id;
227 module = q6apm_graph_get_first_module(apm, dst_sgid);
231 if (module->src_mod_inst_id == iid)
237 int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid, bool connect)
239 struct audioreach_module *module;
243 module = q6apm_graph_get_last_module(apm, src_sgid);
247 iid = module->instance_id;
252 module = q6apm_graph_get_first_module(apm, dst_sgid);
256 /* set src module in dst subgraph first module */
257 module->src_mod_inst_id = iid;
262 int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
263 struct audioreach_module_config *cfg)
265 struct audioreach_module *module;
267 if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
268 module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
270 module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
275 audioreach_set_media_format(graph, module, cfg);
280 EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
282 int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
283 size_t period_sz, unsigned int periods)
285 struct audioreach_graph_data *data;
286 struct audio_buffer *buf;
290 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
291 data = &graph->rx_data;
293 data = &graph->tx_data;
295 mutex_lock(&graph->lock);
298 mutex_unlock(&graph->lock);
302 buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL);
304 mutex_unlock(&graph->lock);
308 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
309 data = &graph->rx_data;
311 data = &graph->tx_data;
316 buf[0].size = period_sz;
318 for (cnt = 1; cnt < periods; cnt++) {
320 buf[cnt].phys = buf[0].phys + (cnt * period_sz);
321 buf[cnt].size = period_sz;
324 data->num_periods = periods;
326 mutex_unlock(&graph->lock);
328 rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
330 dev_err(graph->dev, "Memory_map_regions failed\n");
331 audioreach_graph_free_buf(graph);
336 EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
338 int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
340 struct apm_cmd_shared_mem_unmap_regions *cmd;
341 struct audioreach_graph_data *data;
345 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
346 data = &graph->rx_data;
348 data = &graph->tx_data;
350 if (!data->mem_map_handle)
353 pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
358 cmd = (void *)pkt + GPR_HDR_SIZE;
359 cmd->mem_map_handle = data->mem_map_handle;
361 rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
364 audioreach_graph_free_buf(graph);
368 EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
370 int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
372 struct audioreach_graph_info *info = graph->info;
373 struct audioreach_sub_graph *sgs;
374 struct audioreach_container *container;
375 struct audioreach_module *module;
377 list_for_each_entry(sgs, &info->sg_list, node) {
378 list_for_each_entry(container, &sgs->container_list, node) {
379 list_for_each_entry(module, &container->modules_list, node) {
380 if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||
381 (module->module_id == MODULE_ID_RD_SHARED_MEM_EP))
384 audioreach_set_media_format(graph, module, cfg);
392 EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
394 static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
396 struct audioreach_module *module;
398 module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
402 return module->instance_id;
406 int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
408 struct audioreach_module *module;
410 module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
414 return module->instance_id;
417 EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
419 int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
420 uint32_t lsw_ts, uint32_t wflags)
422 struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
423 struct audio_buffer *ab;
427 iid = q6apm_graph_get_rx_shmem_module_iid(graph);
428 pkt = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
429 graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
430 graph->port->id, iid);
434 write_buffer = (void *)pkt + GPR_HDR_SIZE;
436 mutex_lock(&graph->lock);
437 ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
439 write_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
440 write_buffer->buf_addr_msw = upper_32_bits(ab->phys);
441 write_buffer->buf_size = len;
442 write_buffer->timestamp_lsw = lsw_ts;
443 write_buffer->timestamp_msw = msw_ts;
444 write_buffer->mem_map_handle = graph->rx_data.mem_map_handle;
445 write_buffer->flags = wflags;
447 graph->rx_data.dsp_buf++;
449 if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
450 graph->rx_data.dsp_buf = 0;
452 mutex_unlock(&graph->lock);
454 rc = gpr_send_port_pkt(graph->port, pkt);
460 EXPORT_SYMBOL_GPL(q6apm_write_async);
462 int q6apm_read(struct q6apm_graph *graph)
464 struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;
465 struct audioreach_graph_data *port;
466 struct audio_buffer *ab;
470 iid = q6apm_graph_get_tx_shmem_module_iid(graph);
471 pkt = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
472 graph->tx_data.dsp_buf, graph->port->id, iid);
476 read_buffer = (void *)pkt + GPR_HDR_SIZE;
478 mutex_lock(&graph->lock);
479 port = &graph->tx_data;
480 ab = &port->buf[port->dsp_buf];
482 read_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
483 read_buffer->buf_addr_msw = upper_32_bits(ab->phys);
484 read_buffer->mem_map_handle = port->mem_map_handle;
485 read_buffer->buf_size = ab->size;
489 if (port->dsp_buf >= port->num_periods)
492 mutex_unlock(&graph->lock);
494 rc = gpr_send_port_pkt(graph->port, pkt);
499 EXPORT_SYMBOL_GPL(q6apm_read);
501 static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
503 struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
504 struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
505 struct apm_cmd_rsp_shared_mem_map_regions *rsp;
506 struct gpr_ibasic_rsp_result_t *result;
507 struct q6apm_graph *graph = priv;
508 struct gpr_hdr *hdr = &data->hdr;
509 struct device *dev = graph->dev;
510 uint32_t client_event;
514 result = data->payload;
516 switch (hdr->opcode) {
517 case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
518 client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
519 mutex_lock(&graph->lock);
520 token = hdr->token & APM_WRITE_TOKEN_MASK;
522 done = data->payload;
523 phys = graph->rx_data.buf[token].phys;
524 mutex_unlock(&graph->lock);
526 if (lower_32_bits(phys) == done->buf_addr_lsw &&
527 upper_32_bits(phys) == done->buf_addr_msw) {
528 graph->result.opcode = hdr->opcode;
529 graph->result.status = done->status;
531 graph->cb(client_event, hdr->token, data->payload, graph->priv);
533 dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
538 case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
539 graph->result.opcode = hdr->opcode;
540 graph->result.status = 0;
543 if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
544 graph->rx_data.mem_map_handle = rsp->mem_map_handle;
546 graph->tx_data.mem_map_handle = rsp->mem_map_handle;
548 wake_up(&graph->cmd_wait);
550 case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
551 client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
552 mutex_lock(&graph->lock);
553 rd_done = data->payload;
554 phys = graph->tx_data.buf[hdr->token].phys;
555 mutex_unlock(&graph->lock);
557 if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
558 lower_32_bits(phys) == rd_done->buf_addr_lsw) {
559 graph->result.opcode = hdr->opcode;
560 graph->result.status = rd_done->status;
562 graph->cb(client_event, hdr->token, data->payload, graph->priv);
564 dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
565 rd_done->buf_addr_msw);
568 case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
570 case GPR_BASIC_RSP_RESULT:
571 switch (result->opcode) {
572 case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
573 graph->result.opcode = result->opcode;
574 graph->result.status = 0;
575 if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
576 graph->rx_data.mem_map_handle = 0;
578 graph->tx_data.mem_map_handle = 0;
580 wake_up(&graph->cmd_wait);
582 case APM_CMD_SHARED_MEM_MAP_REGIONS:
583 case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
584 case APM_CMD_SET_CFG:
585 graph->result.opcode = result->opcode;
586 graph->result.status = result->status;
588 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",
589 result->status, result->opcode);
590 wake_up(&graph->cmd_wait);
602 struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
603 void *priv, int graph_id)
605 struct q6apm *apm = dev_get_drvdata(dev->parent);
606 struct audioreach_graph *ar_graph;
607 struct q6apm_graph *graph;
610 ar_graph = q6apm_get_audioreach_graph(apm, graph_id);
611 if (IS_ERR(ar_graph)) {
612 dev_err(dev, "No graph found with id %d\n", graph_id);
613 return ERR_CAST(ar_graph);
616 graph = kzalloc(sizeof(*graph), GFP_KERNEL);
625 graph->info = ar_graph->info;
626 graph->ar_graph = ar_graph;
627 graph->id = ar_graph->id;
630 mutex_init(&graph->lock);
631 init_waitqueue_head(&graph->cmd_wait);
633 graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);
634 if (IS_ERR(graph->port)) {
635 ret = PTR_ERR(graph->port);
644 kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
647 EXPORT_SYMBOL_GPL(q6apm_graph_open);
649 int q6apm_graph_close(struct q6apm_graph *graph)
651 struct audioreach_graph *ar_graph = graph->ar_graph;
653 gpr_free_port(graph->port);
654 kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
659 EXPORT_SYMBOL_GPL(q6apm_graph_close);
661 int q6apm_graph_prepare(struct q6apm_graph *graph)
663 return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
665 EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
667 int q6apm_graph_start(struct q6apm_graph *graph)
669 struct audioreach_graph *ar_graph = graph->ar_graph;
672 if (ar_graph->start_count == 0)
673 ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
675 ar_graph->start_count++;
679 EXPORT_SYMBOL_GPL(q6apm_graph_start);
681 int q6apm_graph_stop(struct q6apm_graph *graph)
683 struct audioreach_graph *ar_graph = graph->ar_graph;
685 if (--ar_graph->start_count > 0)
688 return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);
690 EXPORT_SYMBOL_GPL(q6apm_graph_stop);
692 int q6apm_graph_flush(struct q6apm_graph *graph)
694 return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
696 EXPORT_SYMBOL_GPL(q6apm_graph_flush);
698 static int q6apm_audio_probe(struct snd_soc_component *component)
700 return audioreach_tplg_init(component);
703 static void q6apm_audio_remove(struct snd_soc_component *component)
705 /* remove topology */
706 snd_soc_tplg_component_remove(component);
709 #define APM_AUDIO_DRV_NAME "q6apm-audio"
711 static const struct snd_soc_component_driver q6apm_audio_component = {
712 .name = APM_AUDIO_DRV_NAME,
713 .probe = q6apm_audio_probe,
714 .remove = q6apm_audio_remove,
717 static int apm_probe(gpr_device_t *gdev)
719 struct device *dev = &gdev->dev;
723 apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
727 dev_set_drvdata(dev, apm);
729 mutex_init(&apm->lock);
732 init_waitqueue_head(&apm->wait);
734 INIT_LIST_HEAD(&apm->widget_list);
735 idr_init(&apm->graph_idr);
736 idr_init(&apm->graph_info_idr);
737 idr_init(&apm->sub_graphs_idr);
738 idr_init(&apm->containers_idr);
740 idr_init(&apm->modules_idr);
742 q6apm_get_apm_state(apm);
744 ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
746 dev_err(dev, "failed to get register q6apm: %d\n", ret);
750 return of_platform_populate(dev->of_node, NULL, NULL, dev);
753 struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
755 struct audioreach_graph_info *info = graph->info;
756 struct q6apm *apm = graph->apm;
758 return __q6apm_find_module_by_mid(apm, info, mid);
762 static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
764 gpr_device_t *gdev = priv;
765 struct q6apm *apm = dev_get_drvdata(&gdev->dev);
766 struct device *dev = &gdev->dev;
767 struct gpr_ibasic_rsp_result_t *result;
768 struct gpr_hdr *hdr = &data->hdr;
770 result = data->payload;
772 switch (hdr->opcode) {
773 case APM_CMD_RSP_GET_SPF_STATE:
774 apm->result.opcode = hdr->opcode;
775 apm->result.status = 0;
776 /* First word of result it state */
777 apm->state = result->opcode;
780 case GPR_BASIC_RSP_RESULT:
781 switch (result->opcode) {
782 case APM_CMD_GRAPH_START:
783 case APM_CMD_GRAPH_OPEN:
784 case APM_CMD_GRAPH_PREPARE:
785 case APM_CMD_GRAPH_CLOSE:
786 case APM_CMD_GRAPH_FLUSH:
787 case APM_CMD_GRAPH_STOP:
788 case APM_CMD_SET_CFG:
789 apm->result.opcode = result->opcode;
790 apm->result.status = result->status;
792 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
808 static const struct of_device_id apm_device_id[] = {
809 { .compatible = "qcom,q6apm" },
812 MODULE_DEVICE_TABLE(of, apm_device_id);
815 static gpr_driver_t apm_driver = {
817 .gpr_callback = apm_callback,
820 .of_match_table = of_match_ptr(apm_device_id),
824 module_gpr_driver(apm_driver);
825 MODULE_DESCRIPTION("Audio Process Manager");
826 MODULE_LICENSE("GPL");