ASoC: qdsp6: audioreach: add q6apm support
[platform/kernel/linux-starfive.git] / sound / soc / qcom / qdsp6 / q6apm.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020, Linaro Limited
3
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>
9 #include <linux/of.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"
19 #include "q6apm.h"
20
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[];
26 } __packed;
27
28 #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
29
30 int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
31 {
32         gpr_device_t *gdev = apm->gdev;
33
34         return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
35                                         NULL, &apm->wait, pkt, rsp_opcode);
36 }
37
38 static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, uint32_t graph_id)
39 {
40         struct audioreach_graph_info *info;
41         struct audioreach_graph *graph;
42         int id;
43
44         mutex_lock(&apm->lock);
45         graph = idr_find(&apm->graph_idr, graph_id);
46         mutex_unlock(&apm->lock);
47
48         if (graph) {
49                 kref_get(&graph->refcount);
50                 return graph;
51         }
52
53         info = idr_find(&apm->graph_info_idr, graph_id);
54
55         if (!info)
56                 return ERR_PTR(-ENODEV);
57
58         graph = kzalloc(sizeof(*graph), GFP_KERNEL);
59         if (!graph)
60                 return ERR_PTR(-ENOMEM);
61
62         graph->apm = apm;
63         graph->info = info;
64         graph->id = graph_id;
65
66         graph->graph = audioreach_alloc_graph_pkt(apm, &info->sg_list, graph_id);
67         if (IS_ERR(graph->graph)) {
68                 void *err = graph->graph;
69
70                 kfree(graph);
71                 return ERR_CAST(err);
72         }
73
74         mutex_lock(&apm->lock);
75         id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL);
76         if (id < 0) {
77                 dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id);
78                 kfree(graph);
79                 mutex_unlock(&apm->lock);
80                 return ERR_PTR(id);
81         }
82         mutex_unlock(&apm->lock);
83
84         kref_init(&graph->refcount);
85
86         q6apm_send_cmd_sync(apm, graph->graph, 0);
87
88         return graph;
89 }
90
91 static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
92 {
93         struct audioreach_graph_info *info = graph->info;
94         int num_sub_graphs = info->num_sub_graphs;
95         struct apm_module_param_data *param_data;
96         struct apm_graph_mgmt_cmd *mgmt_cmd;
97         struct audioreach_sub_graph *sg;
98         struct q6apm *apm = graph->apm;
99         int i = 0, rc, payload_size;
100         struct gpr_pkt *pkt;
101
102         payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
103
104         pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
105         if (IS_ERR(pkt))
106                 return PTR_ERR(pkt);
107
108         mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
109
110         mgmt_cmd->num_sub_graphs = num_sub_graphs;
111
112         param_data = &mgmt_cmd->param_data;
113         param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
114         param_data->param_id = APM_PARAM_ID_SUB_GRAPH_LIST;
115         param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
116
117         list_for_each_entry(sg, &info->sg_list, node)
118                 mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id;
119
120         rc = q6apm_send_cmd_sync(apm, pkt, 0);
121
122         kfree(pkt);
123
124         return rc;
125 }
126
127 static void q6apm_put_audioreach_graph(struct kref *ref)
128 {
129         struct audioreach_graph *graph;
130         struct q6apm *apm;
131
132         graph = container_of(ref, struct audioreach_graph, refcount);
133         apm = graph->apm;
134
135         audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
136
137         mutex_lock(&apm->lock);
138         graph = idr_remove(&apm->graph_idr, graph->id);
139         mutex_unlock(&apm->lock);
140
141         kfree(graph->graph);
142         kfree(graph);
143 }
144
145 static int q6apm_get_apm_state(struct q6apm *apm)
146 {
147         struct gpr_pkt *pkt;
148
149         pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0);
150         if (IS_ERR(pkt))
151                 return PTR_ERR(pkt);
152
153         q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
154
155         kfree(pkt);
156
157         return apm->state;
158 }
159
160 static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
161                                                     struct audioreach_graph_info *info,
162                                                     uint32_t mid)
163 {
164         struct audioreach_container *container;
165         struct audioreach_sub_graph *sgs;
166         struct audioreach_module *module;
167
168         list_for_each_entry(sgs, &info->sg_list, node) {
169                 list_for_each_entry(container, &sgs->container_list, node) {
170                         list_for_each_entry(module, &container->modules_list, node) {
171                                 if (mid == module->module_id)
172                                         return module;
173                         }
174                 }
175         }
176
177         return NULL;
178 }
179
180 static struct audioreach_module *q6apm_graph_get_last_module(struct q6apm *apm, u32 sgid)
181 {
182         struct audioreach_container *container;
183         struct audioreach_module *module;
184         struct audioreach_sub_graph *sg;
185
186         mutex_lock(&apm->lock);
187         sg = idr_find(&apm->sub_graphs_idr, sgid);
188         mutex_unlock(&apm->lock);
189         if (!sg)
190                 return NULL;
191
192         container = list_last_entry(&sg->container_list, struct audioreach_container, node);
193         module = audioreach_get_container_last_module(container);
194
195         return module;
196 }
197
198 static struct audioreach_module *q6apm_graph_get_first_module(struct q6apm *apm, u32 sgid)
199 {
200         struct audioreach_container *container;
201         struct audioreach_module *module;
202         struct audioreach_sub_graph *sg;
203
204         mutex_lock(&apm->lock);
205         sg = idr_find(&apm->sub_graphs_idr, sgid);
206         mutex_unlock(&apm->lock);
207         if (!sg)
208                 return NULL;
209
210         container = list_first_entry(&sg->container_list, struct audioreach_container, node);
211         module = audioreach_get_container_first_module(container);
212
213         return module;
214 }
215
216 bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid, u32 dst_sgid)
217 {
218         struct audioreach_module *module;
219         u32 iid;
220
221         module = q6apm_graph_get_last_module(apm, src_sgid);
222         if (!module)
223                 return false;
224
225         iid = module->instance_id;
226         module = q6apm_graph_get_first_module(apm, dst_sgid);
227         if (!module)
228                 return false;
229
230         if (module->src_mod_inst_id == iid)
231                 return true;
232
233         return false;
234 }
235
236 int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid, bool connect)
237 {
238         struct audioreach_module *module;
239         u32 iid;
240
241         if (connect) {
242                 module = q6apm_graph_get_last_module(apm, src_sgid);
243                 if (!module)
244                         return -ENODEV;
245
246                 iid = module->instance_id;
247         } else {
248                 iid = 0;
249         }
250
251         module = q6apm_graph_get_first_module(apm, dst_sgid);
252         if (!module)
253                 return -ENODEV;
254
255         /* set src module in dst subgraph first module */
256         module->src_mod_inst_id = iid;
257
258         return 0;
259 }
260
261 int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
262 {
263         struct audioreach_module *module;
264
265         module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
266         if (!module)
267                 return -ENODEV;
268
269         return module->instance_id;
270
271 }
272 EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
273
274 static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
275 {
276         struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
277         struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
278         struct apm_cmd_rsp_shared_mem_map_regions *rsp;
279         struct gpr_ibasic_rsp_result_t *result;
280         struct q6apm_graph *graph = priv;
281         struct gpr_hdr *hdr = &data->hdr;
282         struct device *dev = graph->dev;
283         uint32_t client_event;
284         phys_addr_t phys;
285         int token;
286
287         result = data->payload;
288
289         switch (hdr->opcode) {
290         case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
291                 client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
292                 mutex_lock(&graph->lock);
293                 token = hdr->token & APM_WRITE_TOKEN_MASK;
294
295                 done = data->payload;
296                 phys = graph->rx_data.buf[token].phys;
297                 mutex_unlock(&graph->lock);
298
299                 if (lower_32_bits(phys) == done->buf_addr_lsw &&
300                     upper_32_bits(phys) == done->buf_addr_msw) {
301                         graph->result.opcode = hdr->opcode;
302                         graph->result.status = done->status;
303                         if (graph->cb)
304                                 graph->cb(client_event, hdr->token, data->payload, graph->priv);
305                 } else {
306                         dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
307                                 done->buf_addr_msw);
308                 }
309
310                 break;
311         case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
312                 graph->result.opcode = hdr->opcode;
313                 graph->result.status = 0;
314                 rsp = data->payload;
315
316                 if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
317                         graph->rx_data.mem_map_handle = rsp->mem_map_handle;
318                 else
319                         graph->tx_data.mem_map_handle = rsp->mem_map_handle;
320
321                 wake_up(&graph->cmd_wait);
322                 break;
323         case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
324                 client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
325                 mutex_lock(&graph->lock);
326                 rd_done = data->payload;
327                 phys = graph->tx_data.buf[hdr->token].phys;
328                 mutex_unlock(&graph->lock);
329
330                 if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
331                     lower_32_bits(phys) == rd_done->buf_addr_lsw) {
332                         graph->result.opcode = hdr->opcode;
333                         graph->result.status = rd_done->status;
334                         if (graph->cb)
335                                 graph->cb(client_event, hdr->token, data->payload, graph->priv);
336                 } else {
337                         dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
338                                 rd_done->buf_addr_msw);
339                 }
340                 break;
341         case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
342                 break;
343         case GPR_BASIC_RSP_RESULT:
344                 switch (result->opcode) {
345                 case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
346                         graph->result.opcode = result->opcode;
347                         graph->result.status = 0;
348                         if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
349                                 graph->rx_data.mem_map_handle = 0;
350                         else
351                                 graph->tx_data.mem_map_handle = 0;
352
353                         wake_up(&graph->cmd_wait);
354                         break;
355                 case APM_CMD_SHARED_MEM_MAP_REGIONS:
356                 case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
357                 case APM_CMD_SET_CFG:
358                         graph->result.opcode = result->opcode;
359                         graph->result.status = result->status;
360                         if (result->status)
361                                 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",
362                                         result->status, result->opcode);
363                         wake_up(&graph->cmd_wait);
364                         break;
365                 default:
366                         break;
367                 }
368                 break;
369         default:
370                 break;
371         }
372         return 0;
373 }
374
375 struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
376                                      void *priv, int graph_id)
377 {
378         struct q6apm *apm = dev_get_drvdata(dev->parent);
379         struct audioreach_graph *ar_graph;
380         struct q6apm_graph *graph;
381         int ret;
382
383         ar_graph = q6apm_get_audioreach_graph(apm, graph_id);
384         if (IS_ERR(ar_graph)) {
385                 dev_err(dev, "No graph found with id %d\n", graph_id);
386                 return ERR_CAST(ar_graph);
387         }
388
389         graph = kzalloc(sizeof(*graph), GFP_KERNEL);
390         if (!graph) {
391                 ret = -ENOMEM;
392                 goto err;
393         }
394
395         graph->apm = apm;
396         graph->priv = priv;
397         graph->cb = cb;
398         graph->info = ar_graph->info;
399         graph->ar_graph = ar_graph;
400         graph->id = ar_graph->id;
401         graph->dev = dev;
402
403         mutex_init(&graph->lock);
404         init_waitqueue_head(&graph->cmd_wait);
405
406         graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);
407         if (!graph->port) {
408                 kfree(graph);
409                 ret = -ENOMEM;
410                 goto err;
411         }
412
413         return graph;
414 err:
415         kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
416         return ERR_PTR(ret);
417 }
418 EXPORT_SYMBOL_GPL(q6apm_graph_open);
419
420 int q6apm_graph_close(struct q6apm_graph *graph)
421 {
422         struct audioreach_graph *ar_graph = graph->ar_graph;
423
424         gpr_free_port(graph->port);
425         kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
426         kfree(graph);
427
428         return 0;
429 }
430 EXPORT_SYMBOL_GPL(q6apm_graph_close);
431
432 int q6apm_graph_prepare(struct q6apm_graph *graph)
433 {
434         return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
435 }
436 EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
437
438 int q6apm_graph_start(struct q6apm_graph *graph)
439 {
440         struct audioreach_graph *ar_graph = graph->ar_graph;
441         int ret = 0;
442
443         if (ar_graph->start_count == 0)
444                 ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
445
446         ar_graph->start_count++;
447
448         return ret;
449 }
450 EXPORT_SYMBOL_GPL(q6apm_graph_start);
451
452 int q6apm_graph_stop(struct q6apm_graph *graph)
453 {
454         struct audioreach_graph *ar_graph = graph->ar_graph;
455
456         if (--ar_graph->start_count > 0)
457                 return 0;
458
459         return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);
460 }
461 EXPORT_SYMBOL_GPL(q6apm_graph_stop);
462
463 int q6apm_graph_flush(struct q6apm_graph *graph)
464 {
465         return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
466 }
467 EXPORT_SYMBOL_GPL(q6apm_graph_flush);
468
469 static int q6apm_audio_probe(struct snd_soc_component *component)
470 {
471         return 0;
472 }
473
474 static void q6apm_audio_remove(struct snd_soc_component *component)
475 {
476         /* remove topology */
477         snd_soc_tplg_component_remove(component);
478 }
479
480 #define APM_AUDIO_DRV_NAME "q6apm-audio"
481
482 static const struct snd_soc_component_driver q6apm_audio_component = {
483         .name           = APM_AUDIO_DRV_NAME,
484         .probe          = q6apm_audio_probe,
485         .remove         = q6apm_audio_remove,
486 };
487
488 static int apm_probe(gpr_device_t *gdev)
489 {
490         struct device *dev = &gdev->dev;
491         struct q6apm *apm;
492         int ret;
493
494         apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
495         if (!apm)
496                 return -ENOMEM;
497
498         dev_set_drvdata(dev, apm);
499
500         mutex_init(&apm->lock);
501         apm->dev = dev;
502         apm->gdev = gdev;
503         init_waitqueue_head(&apm->wait);
504
505         idr_init(&apm->graph_idr);
506         idr_init(&apm->graph_info_idr);
507         idr_init(&apm->sub_graphs_idr);
508         idr_init(&apm->containers_idr);
509
510         idr_init(&apm->modules_idr);
511
512         q6apm_get_apm_state(apm);
513
514         ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
515         if (ret < 0) {
516                 dev_err(dev, "failed to get register q6apm: %d\n", ret);
517                 return ret;
518         }
519
520         return of_platform_populate(dev->of_node, NULL, NULL, dev);
521 }
522
523 struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
524 {
525         struct audioreach_graph_info *info = graph->info;
526         struct q6apm *apm = graph->apm;
527
528         return __q6apm_find_module_by_mid(apm, info, mid);
529
530 }
531
532 static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
533 {
534         gpr_device_t *gdev = priv;
535         struct q6apm *apm = dev_get_drvdata(&gdev->dev);
536         struct device *dev = &gdev->dev;
537         struct gpr_ibasic_rsp_result_t *result;
538         struct gpr_hdr *hdr = &data->hdr;
539
540         result = data->payload;
541
542         switch (hdr->opcode) {
543         case APM_CMD_RSP_GET_SPF_STATE:
544                 apm->result.opcode = hdr->opcode;
545                 apm->result.status = 0;
546                 /* First word of result it state */
547                 apm->state = result->opcode;
548                 wake_up(&apm->wait);
549                 break;
550         case GPR_BASIC_RSP_RESULT:
551                 switch (result->opcode) {
552                 case APM_CMD_GRAPH_START:
553                 case APM_CMD_GRAPH_OPEN:
554                 case APM_CMD_GRAPH_PREPARE:
555                 case APM_CMD_GRAPH_CLOSE:
556                 case APM_CMD_GRAPH_FLUSH:
557                 case APM_CMD_GRAPH_STOP:
558                 case APM_CMD_SET_CFG:
559                         apm->result.opcode = result->opcode;
560                         apm->result.status = result->status;
561                         if (result->status)
562                                 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
563                                         result->opcode);
564                         wake_up(&apm->wait);
565                         break;
566                 default:
567                         break;
568                 }
569                 break;
570         default:
571                 break;
572         }
573
574         return 0;
575 }
576
577 #ifdef CONFIG_OF
578 static const struct of_device_id apm_device_id[]  = {
579         { .compatible = "qcom,q6apm" },
580         {},
581 };
582 MODULE_DEVICE_TABLE(of, apm_device_id);
583 #endif
584
585 static gpr_driver_t apm_driver = {
586         .probe = apm_probe,
587         .gpr_callback = apm_callback,
588         .driver = {
589                 .name = "qcom-apm",
590                 .of_match_table = of_match_ptr(apm_device_id),
591         },
592 };
593
594 module_gpr_driver(apm_driver);
595 MODULE_DESCRIPTION("Audio Process Manager");
596 MODULE_LICENSE("GPL");