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
q6apm_send_cmd_sync(struct q6apm * apm,struct gpr_pkt * pkt,uint32_t rsp_opcode)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
q6apm_get_audioreach_graph(struct q6apm * apm,uint32_t graph_id)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->graph);
79 kfree(graph);
80 mutex_unlock(&apm->lock);
81 return ERR_PTR(id);
82 }
83 mutex_unlock(&apm->lock);
84
85 kref_init(&graph->refcount);
86
87 q6apm_send_cmd_sync(apm, graph->graph, 0);
88
89 return graph;
90 }
91
audioreach_graph_mgmt_cmd(struct audioreach_graph * graph,uint32_t opcode)92 static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
93 {
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;
101 struct gpr_pkt *pkt;
102
103 payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
104
105 pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
106 if (IS_ERR(pkt))
107 return PTR_ERR(pkt);
108
109 mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
110
111 mgmt_cmd->num_sub_graphs = num_sub_graphs;
112
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;
117
118 list_for_each_entry(sg, &info->sg_list, node)
119 mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id;
120
121 rc = q6apm_send_cmd_sync(apm, pkt, 0);
122
123 kfree(pkt);
124
125 return rc;
126 }
127
q6apm_put_audioreach_graph(struct kref * ref)128 static void q6apm_put_audioreach_graph(struct kref *ref)
129 {
130 struct audioreach_graph *graph;
131 struct q6apm *apm;
132
133 graph = container_of(ref, struct audioreach_graph, refcount);
134 apm = graph->apm;
135
136 audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
137
138 mutex_lock(&apm->lock);
139 graph = idr_remove(&apm->graph_idr, graph->id);
140 mutex_unlock(&apm->lock);
141
142 kfree(graph->graph);
143 kfree(graph);
144 }
145
q6apm_get_apm_state(struct q6apm * apm)146 static int q6apm_get_apm_state(struct q6apm *apm)
147 {
148 struct gpr_pkt *pkt;
149
150 pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_GET_SPF_STATE, 0);
151 if (IS_ERR(pkt))
152 return PTR_ERR(pkt);
153
154 q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
155
156 kfree(pkt);
157
158 return apm->state;
159 }
160
__q6apm_find_module_by_mid(struct q6apm * apm,struct audioreach_graph_info * info,uint32_t mid)161 static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
162 struct audioreach_graph_info *info,
163 uint32_t mid)
164 {
165 struct audioreach_container *container;
166 struct audioreach_sub_graph *sgs;
167 struct audioreach_module *module;
168
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)
173 return module;
174 }
175 }
176 }
177
178 return NULL;
179 }
180
q6apm_graph_get_last_module(struct q6apm * apm,u32 sgid)181 static struct audioreach_module *q6apm_graph_get_last_module(struct q6apm *apm, u32 sgid)
182 {
183 struct audioreach_container *container;
184 struct audioreach_module *module;
185 struct audioreach_sub_graph *sg;
186
187 mutex_lock(&apm->lock);
188 sg = idr_find(&apm->sub_graphs_idr, sgid);
189 mutex_unlock(&apm->lock);
190 if (!sg)
191 return NULL;
192
193 container = list_last_entry(&sg->container_list, struct audioreach_container, node);
194 module = audioreach_get_container_last_module(container);
195
196 return module;
197 }
198
q6apm_graph_get_first_module(struct q6apm * apm,u32 sgid)199 static struct audioreach_module *q6apm_graph_get_first_module(struct q6apm *apm, u32 sgid)
200 {
201 struct audioreach_container *container;
202 struct audioreach_module *module;
203 struct audioreach_sub_graph *sg;
204
205 mutex_lock(&apm->lock);
206 sg = idr_find(&apm->sub_graphs_idr, sgid);
207 mutex_unlock(&apm->lock);
208 if (!sg)
209 return NULL;
210
211 container = list_first_entry(&sg->container_list, struct audioreach_container, node);
212 module = audioreach_get_container_first_module(container);
213
214 return module;
215 }
216
q6apm_is_sub_graphs_connected(struct q6apm * apm,u32 src_sgid,u32 dst_sgid)217 bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid, u32 dst_sgid)
218 {
219 struct audioreach_module *module;
220 u32 iid;
221
222 module = q6apm_graph_get_last_module(apm, src_sgid);
223 if (!module)
224 return false;
225
226 iid = module->instance_id;
227 module = q6apm_graph_get_first_module(apm, dst_sgid);
228 if (!module)
229 return false;
230
231 if (module->src_mod_inst_id == iid)
232 return true;
233
234 return false;
235 }
236
q6apm_connect_sub_graphs(struct q6apm * apm,u32 src_sgid,u32 dst_sgid,bool connect)237 int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid, bool connect)
238 {
239 struct audioreach_module *module;
240 u32 iid;
241
242 if (connect) {
243 module = q6apm_graph_get_last_module(apm, src_sgid);
244 if (!module)
245 return -ENODEV;
246
247 iid = module->instance_id;
248 } else {
249 iid = 0;
250 }
251
252 module = q6apm_graph_get_first_module(apm, dst_sgid);
253 if (!module)
254 return -ENODEV;
255
256 /* set src module in dst subgraph first module */
257 module->src_mod_inst_id = iid;
258
259 return 0;
260 }
261
q6apm_graph_media_format_shmem(struct q6apm_graph * graph,struct audioreach_module_config * cfg)262 int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
263 struct audioreach_module_config *cfg)
264 {
265 struct audioreach_module *module;
266
267 if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
268 module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
269 else
270 module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
271
272 if (!module)
273 return -ENODEV;
274
275 audioreach_set_media_format(graph, module, cfg);
276
277 return 0;
278
279 }
280 EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
281
q6apm_map_memory_regions(struct q6apm_graph * graph,unsigned int dir,phys_addr_t phys,size_t period_sz,unsigned int periods)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)
284 {
285 struct audioreach_graph_data *data;
286 struct audio_buffer *buf;
287 int cnt;
288 int rc;
289
290 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
291 data = &graph->rx_data;
292 else
293 data = &graph->tx_data;
294
295 mutex_lock(&graph->lock);
296
297 if (data->buf) {
298 mutex_unlock(&graph->lock);
299 return 0;
300 }
301
302 buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL);
303 if (!buf) {
304 mutex_unlock(&graph->lock);
305 return -ENOMEM;
306 }
307
308 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
309 data = &graph->rx_data;
310 else
311 data = &graph->tx_data;
312
313 data->buf = buf;
314
315 buf[0].phys = phys;
316 buf[0].size = period_sz;
317
318 for (cnt = 1; cnt < periods; cnt++) {
319 if (period_sz > 0) {
320 buf[cnt].phys = buf[0].phys + (cnt * period_sz);
321 buf[cnt].size = period_sz;
322 }
323 }
324 data->num_periods = periods;
325
326 mutex_unlock(&graph->lock);
327
328 rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
329 if (rc < 0) {
330 dev_err(graph->dev, "Memory_map_regions failed\n");
331 audioreach_graph_free_buf(graph);
332 }
333
334 return rc;
335 }
336 EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
337
q6apm_unmap_memory_regions(struct q6apm_graph * graph,unsigned int dir)338 int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
339 {
340 struct apm_cmd_shared_mem_unmap_regions *cmd;
341 struct audioreach_graph_data *data;
342 struct gpr_pkt *pkt;
343 int rc;
344
345 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
346 data = &graph->rx_data;
347 else
348 data = &graph->tx_data;
349
350 if (!data->mem_map_handle)
351 return 0;
352
353 pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
354 graph->port->id);
355 if (IS_ERR(pkt))
356 return PTR_ERR(pkt);
357
358 cmd = (void *)pkt + GPR_HDR_SIZE;
359 cmd->mem_map_handle = data->mem_map_handle;
360
361 rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
362 kfree(pkt);
363
364 audioreach_graph_free_buf(graph);
365
366 return rc;
367 }
368 EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
369
q6apm_graph_media_format_pcm(struct q6apm_graph * graph,struct audioreach_module_config * cfg)370 int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
371 {
372 struct audioreach_graph_info *info = graph->info;
373 struct audioreach_sub_graph *sgs;
374 struct audioreach_container *container;
375 struct audioreach_module *module;
376
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))
382 continue;
383
384 audioreach_set_media_format(graph, module, cfg);
385 }
386 }
387 }
388
389 return 0;
390
391 }
392 EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
393
q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph * graph)394 static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
395 {
396 struct audioreach_module *module;
397
398 module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
399 if (!module)
400 return -ENODEV;
401
402 return module->instance_id;
403
404 }
405
q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph * graph)406 int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
407 {
408 struct audioreach_module *module;
409
410 module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
411 if (!module)
412 return -ENODEV;
413
414 return module->instance_id;
415
416 }
417 EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
418
q6apm_write_async(struct q6apm_graph * graph,uint32_t len,uint32_t msw_ts,uint32_t lsw_ts,uint32_t wflags)419 int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
420 uint32_t lsw_ts, uint32_t wflags)
421 {
422 struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
423 struct audio_buffer *ab;
424 struct gpr_pkt *pkt;
425 int rc, iid;
426
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);
431 if (IS_ERR(pkt))
432 return PTR_ERR(pkt);
433
434 write_buffer = (void *)pkt + GPR_HDR_SIZE;
435
436 mutex_lock(&graph->lock);
437 ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
438
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;
446
447 graph->rx_data.dsp_buf++;
448
449 if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
450 graph->rx_data.dsp_buf = 0;
451
452 mutex_unlock(&graph->lock);
453
454 rc = gpr_send_port_pkt(graph->port, pkt);
455
456 kfree(pkt);
457
458 return rc;
459 }
460 EXPORT_SYMBOL_GPL(q6apm_write_async);
461
q6apm_read(struct q6apm_graph * graph)462 int q6apm_read(struct q6apm_graph *graph)
463 {
464 struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;
465 struct audioreach_graph_data *port;
466 struct audio_buffer *ab;
467 struct gpr_pkt *pkt;
468 int rc, iid;
469
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);
473 if (IS_ERR(pkt))
474 return PTR_ERR(pkt);
475
476 read_buffer = (void *)pkt + GPR_HDR_SIZE;
477
478 mutex_lock(&graph->lock);
479 port = &graph->tx_data;
480 ab = &port->buf[port->dsp_buf];
481
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;
486
487 port->dsp_buf++;
488
489 if (port->dsp_buf >= port->num_periods)
490 port->dsp_buf = 0;
491
492 mutex_unlock(&graph->lock);
493
494 rc = gpr_send_port_pkt(graph->port, pkt);
495 kfree(pkt);
496
497 return rc;
498 }
499 EXPORT_SYMBOL_GPL(q6apm_read);
500
graph_callback(struct gpr_resp_pkt * data,void * priv,int op)501 static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
502 {
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;
511 phys_addr_t phys;
512 int token;
513
514 result = data->payload;
515
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;
521
522 done = data->payload;
523 phys = graph->rx_data.buf[token].phys;
524 mutex_unlock(&graph->lock);
525
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;
530 if (graph->cb)
531 graph->cb(client_event, hdr->token, data->payload, graph->priv);
532 } else {
533 dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
534 done->buf_addr_msw);
535 }
536
537 break;
538 case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
539 graph->result.opcode = hdr->opcode;
540 graph->result.status = 0;
541 rsp = data->payload;
542
543 if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
544 graph->rx_data.mem_map_handle = rsp->mem_map_handle;
545 else
546 graph->tx_data.mem_map_handle = rsp->mem_map_handle;
547
548 wake_up(&graph->cmd_wait);
549 break;
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);
556
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;
561 if (graph->cb)
562 graph->cb(client_event, hdr->token, data->payload, graph->priv);
563 } else {
564 dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
565 rd_done->buf_addr_msw);
566 }
567 break;
568 case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
569 break;
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;
577 else
578 graph->tx_data.mem_map_handle = 0;
579
580 wake_up(&graph->cmd_wait);
581 break;
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;
587 if (result->status)
588 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",
589 result->status, result->opcode);
590 wake_up(&graph->cmd_wait);
591 break;
592 default:
593 break;
594 }
595 break;
596 default:
597 break;
598 }
599 return 0;
600 }
601
q6apm_graph_open(struct device * dev,q6apm_cb cb,void * priv,int graph_id)602 struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
603 void *priv, int graph_id)
604 {
605 struct q6apm *apm = dev_get_drvdata(dev->parent);
606 struct audioreach_graph *ar_graph;
607 struct q6apm_graph *graph;
608 int ret;
609
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);
614 }
615
616 graph = kzalloc(sizeof(*graph), GFP_KERNEL);
617 if (!graph) {
618 ret = -ENOMEM;
619 goto put_ar_graph;
620 }
621
622 graph->apm = apm;
623 graph->priv = priv;
624 graph->cb = cb;
625 graph->info = ar_graph->info;
626 graph->ar_graph = ar_graph;
627 graph->id = ar_graph->id;
628 graph->dev = dev;
629
630 mutex_init(&graph->lock);
631 init_waitqueue_head(&graph->cmd_wait);
632
633 graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);
634 if (IS_ERR(graph->port)) {
635 ret = PTR_ERR(graph->port);
636 goto free_graph;
637 }
638
639 return graph;
640
641 free_graph:
642 kfree(graph);
643 put_ar_graph:
644 kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
645 return ERR_PTR(ret);
646 }
647 EXPORT_SYMBOL_GPL(q6apm_graph_open);
648
q6apm_graph_close(struct q6apm_graph * graph)649 int q6apm_graph_close(struct q6apm_graph *graph)
650 {
651 struct audioreach_graph *ar_graph = graph->ar_graph;
652
653 gpr_free_port(graph->port);
654 kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
655 kfree(graph);
656
657 return 0;
658 }
659 EXPORT_SYMBOL_GPL(q6apm_graph_close);
660
q6apm_graph_prepare(struct q6apm_graph * graph)661 int q6apm_graph_prepare(struct q6apm_graph *graph)
662 {
663 return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
664 }
665 EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
666
q6apm_graph_start(struct q6apm_graph * graph)667 int q6apm_graph_start(struct q6apm_graph *graph)
668 {
669 struct audioreach_graph *ar_graph = graph->ar_graph;
670 int ret = 0;
671
672 if (ar_graph->start_count == 0)
673 ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
674
675 ar_graph->start_count++;
676
677 return ret;
678 }
679 EXPORT_SYMBOL_GPL(q6apm_graph_start);
680
q6apm_graph_stop(struct q6apm_graph * graph)681 int q6apm_graph_stop(struct q6apm_graph *graph)
682 {
683 struct audioreach_graph *ar_graph = graph->ar_graph;
684
685 if (--ar_graph->start_count > 0)
686 return 0;
687
688 return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);
689 }
690 EXPORT_SYMBOL_GPL(q6apm_graph_stop);
691
q6apm_graph_flush(struct q6apm_graph * graph)692 int q6apm_graph_flush(struct q6apm_graph *graph)
693 {
694 return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
695 }
696 EXPORT_SYMBOL_GPL(q6apm_graph_flush);
697
q6apm_audio_probe(struct snd_soc_component * component)698 static int q6apm_audio_probe(struct snd_soc_component *component)
699 {
700 return audioreach_tplg_init(component);
701 }
702
q6apm_audio_remove(struct snd_soc_component * component)703 static void q6apm_audio_remove(struct snd_soc_component *component)
704 {
705 /* remove topology */
706 snd_soc_tplg_component_remove(component);
707 }
708
709 #define APM_AUDIO_DRV_NAME "q6apm-audio"
710
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,
715 };
716
apm_probe(gpr_device_t * gdev)717 static int apm_probe(gpr_device_t *gdev)
718 {
719 struct device *dev = &gdev->dev;
720 struct q6apm *apm;
721 int ret;
722
723 apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
724 if (!apm)
725 return -ENOMEM;
726
727 dev_set_drvdata(dev, apm);
728
729 mutex_init(&apm->lock);
730 apm->dev = dev;
731 apm->gdev = gdev;
732 init_waitqueue_head(&apm->wait);
733
734 idr_init(&apm->graph_idr);
735 idr_init(&apm->graph_info_idr);
736 idr_init(&apm->sub_graphs_idr);
737 idr_init(&apm->containers_idr);
738
739 idr_init(&apm->modules_idr);
740
741 q6apm_get_apm_state(apm);
742
743 ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
744 if (ret < 0) {
745 dev_err(dev, "failed to get register q6apm: %d\n", ret);
746 return ret;
747 }
748
749 return of_platform_populate(dev->of_node, NULL, NULL, dev);
750 }
751
q6apm_find_module_by_mid(struct q6apm_graph * graph,uint32_t mid)752 struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
753 {
754 struct audioreach_graph_info *info = graph->info;
755 struct q6apm *apm = graph->apm;
756
757 return __q6apm_find_module_by_mid(apm, info, mid);
758
759 }
760
apm_callback(struct gpr_resp_pkt * data,void * priv,int op)761 static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
762 {
763 gpr_device_t *gdev = priv;
764 struct q6apm *apm = dev_get_drvdata(&gdev->dev);
765 struct device *dev = &gdev->dev;
766 struct gpr_ibasic_rsp_result_t *result;
767 struct gpr_hdr *hdr = &data->hdr;
768
769 result = data->payload;
770
771 switch (hdr->opcode) {
772 case APM_CMD_RSP_GET_SPF_STATE:
773 apm->result.opcode = hdr->opcode;
774 apm->result.status = 0;
775 /* First word of result it state */
776 apm->state = result->opcode;
777 wake_up(&apm->wait);
778 break;
779 case GPR_BASIC_RSP_RESULT:
780 switch (result->opcode) {
781 case APM_CMD_GRAPH_START:
782 case APM_CMD_GRAPH_OPEN:
783 case APM_CMD_GRAPH_PREPARE:
784 case APM_CMD_GRAPH_CLOSE:
785 case APM_CMD_GRAPH_FLUSH:
786 case APM_CMD_GRAPH_STOP:
787 case APM_CMD_SET_CFG:
788 apm->result.opcode = result->opcode;
789 apm->result.status = result->status;
790 if (result->status)
791 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
792 result->opcode);
793 wake_up(&apm->wait);
794 break;
795 default:
796 break;
797 }
798 break;
799 default:
800 break;
801 }
802
803 return 0;
804 }
805
806 #ifdef CONFIG_OF
807 static const struct of_device_id apm_device_id[] = {
808 { .compatible = "qcom,q6apm" },
809 {},
810 };
811 MODULE_DEVICE_TABLE(of, apm_device_id);
812 #endif
813
814 static gpr_driver_t apm_driver = {
815 .probe = apm_probe,
816 .gpr_callback = apm_callback,
817 .driver = {
818 .name = "qcom-apm",
819 .of_match_table = of_match_ptr(apm_device_id),
820 },
821 };
822
823 module_gpr_driver(apm_driver);
824 MODULE_DESCRIPTION("Audio Process Manager");
825 MODULE_LICENSE("GPL");
826