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 static struct q6apm *g_apm;
31
q6apm_send_cmd_sync(struct q6apm * apm,struct gpr_pkt * pkt,uint32_t rsp_opcode)32 int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
33 {
34 gpr_device_t *gdev = apm->gdev;
35
36 return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock,
37 NULL, &apm->wait, pkt, rsp_opcode);
38 }
39
q6apm_get_audioreach_graph(struct q6apm * apm,uint32_t graph_id)40 static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, uint32_t graph_id)
41 {
42 struct audioreach_graph_info *info;
43 struct audioreach_graph *graph;
44 int id;
45
46 mutex_lock(&apm->lock);
47 graph = idr_find(&apm->graph_idr, graph_id);
48 mutex_unlock(&apm->lock);
49
50 if (graph) {
51 kref_get(&graph->refcount);
52 return graph;
53 }
54
55 info = idr_find(&apm->graph_info_idr, graph_id);
56
57 if (!info)
58 return ERR_PTR(-ENODEV);
59
60 graph = kzalloc_obj(*graph);
61 if (!graph)
62 return ERR_PTR(-ENOMEM);
63
64 graph->apm = apm;
65 graph->info = info;
66 graph->id = graph_id;
67
68 graph->graph = audioreach_alloc_graph_pkt(apm, info);
69 if (IS_ERR(graph->graph)) {
70 void *err = graph->graph;
71
72 kfree(graph);
73 return ERR_CAST(err);
74 }
75
76 mutex_lock(&apm->lock);
77 id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL);
78 if (id < 0) {
79 dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id);
80 kfree(graph->graph);
81 kfree(graph);
82 mutex_unlock(&apm->lock);
83 return ERR_PTR(id);
84 }
85 mutex_unlock(&apm->lock);
86
87 kref_init(&graph->refcount);
88
89 q6apm_send_cmd_sync(apm, graph->graph, 0);
90
91 return graph;
92 }
93
audioreach_graph_mgmt_cmd(struct audioreach_graph * graph,uint32_t opcode)94 static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
95 {
96 struct audioreach_graph_info *info = graph->info;
97 int num_sub_graphs = info->num_sub_graphs;
98 struct apm_module_param_data *param_data;
99 struct apm_graph_mgmt_cmd *mgmt_cmd;
100 struct audioreach_sub_graph *sg;
101 struct q6apm *apm = graph->apm;
102 int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
103
104 struct gpr_pkt *pkt __free(kfree) = 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 return q6apm_send_cmd_sync(apm, pkt, 0);
121 }
122
q6apm_put_audioreach_graph(struct kref * ref)123 static void q6apm_put_audioreach_graph(struct kref *ref)
124 {
125 struct audioreach_graph *graph;
126 struct q6apm *apm;
127
128 graph = container_of(ref, struct audioreach_graph, refcount);
129 apm = graph->apm;
130
131 audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
132
133 mutex_lock(&apm->lock);
134 graph = idr_remove(&apm->graph_idr, graph->id);
135 mutex_unlock(&apm->lock);
136
137 kfree(graph->graph);
138 kfree(graph);
139 }
140
141
q6apm_get_apm_state(struct q6apm * apm)142 static int q6apm_get_apm_state(struct q6apm *apm)
143 {
144 struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(0,
145 APM_CMD_GET_SPF_STATE, 0);
146 if (IS_ERR(pkt))
147 return PTR_ERR(pkt);
148
149 q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
150
151 return apm->state;
152 }
153
q6apm_is_adsp_ready(void)154 bool q6apm_is_adsp_ready(void)
155 {
156 if (g_apm)
157 return q6apm_get_apm_state(g_apm);
158
159 return false;
160 }
161 EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready);
162
__q6apm_find_module_by_mid(struct q6apm * apm,struct audioreach_graph_info * info,uint32_t mid)163 static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
164 struct audioreach_graph_info *info,
165 uint32_t mid)
166 {
167 struct audioreach_container *container;
168 struct audioreach_sub_graph *sgs;
169 struct audioreach_module *module;
170
171 list_for_each_entry(sgs, &info->sg_list, node) {
172 list_for_each_entry(container, &sgs->container_list, node) {
173 list_for_each_entry(module, &container->modules_list, node) {
174 if (mid == module->module_id)
175 return module;
176 }
177 }
178 }
179
180 return NULL;
181 }
182
q6apm_graph_media_format_shmem(struct q6apm_graph * graph,struct audioreach_module_config * cfg)183 int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
184 struct audioreach_module_config *cfg)
185 {
186 struct audioreach_module *module;
187
188 if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
189 module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
190 else
191 module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
192
193 if (!module)
194 return -ENODEV;
195
196 audioreach_set_media_format(graph, module, cfg);
197
198 return 0;
199
200 }
201 EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
202
q6apm_map_memory_regions(struct q6apm_graph * graph,unsigned int dir,phys_addr_t phys,size_t period_sz,unsigned int periods)203 int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
204 size_t period_sz, unsigned int periods)
205 {
206 struct audioreach_graph_data *data;
207 struct audio_buffer *buf;
208 int cnt;
209 int rc;
210
211 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
212 data = &graph->rx_data;
213 else
214 data = &graph->tx_data;
215
216 mutex_lock(&graph->lock);
217
218 if (data->buf) {
219 mutex_unlock(&graph->lock);
220 return 0;
221 }
222
223 buf = kzalloc_objs(struct audio_buffer, periods);
224 if (!buf) {
225 mutex_unlock(&graph->lock);
226 return -ENOMEM;
227 }
228
229 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
230 data = &graph->rx_data;
231 else
232 data = &graph->tx_data;
233
234 data->buf = buf;
235
236 buf[0].phys = phys;
237 buf[0].size = period_sz;
238
239 for (cnt = 1; cnt < periods; cnt++) {
240 if (period_sz > 0) {
241 buf[cnt].phys = buf[0].phys + (cnt * period_sz);
242 buf[cnt].size = period_sz;
243 }
244 }
245 data->num_periods = periods;
246
247 mutex_unlock(&graph->lock);
248
249 rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
250 if (rc < 0) {
251 dev_err(graph->dev, "Memory_map_regions failed\n");
252 audioreach_graph_free_buf(graph);
253 }
254
255 return rc;
256 }
257 EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
258
q6apm_unmap_memory_regions(struct q6apm_graph * graph,unsigned int dir)259 int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
260 {
261 struct apm_cmd_shared_mem_unmap_regions *cmd;
262 struct audioreach_graph_data *data;
263 int rc;
264
265 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
266 data = &graph->rx_data;
267 else
268 data = &graph->tx_data;
269
270 if (!data->mem_map_handle)
271 return 0;
272
273 struct gpr_pkt *pkt __free(kfree) =
274 audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS,
275 dir, graph->port->id);
276 if (IS_ERR(pkt))
277 return PTR_ERR(pkt);
278
279 cmd = (void *)pkt + GPR_HDR_SIZE;
280 cmd->mem_map_handle = data->mem_map_handle;
281
282 rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
283
284 audioreach_graph_free_buf(graph);
285
286 return rc;
287 }
288 EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
289
q6apm_remove_initial_silence(struct device * dev,struct q6apm_graph * graph,uint32_t samples)290 int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
291 {
292 struct audioreach_module *module;
293
294 module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
295 if (!module)
296 return -ENODEV;
297
298 return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples);
299 }
300 EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence);
301
q6apm_remove_trailing_silence(struct device * dev,struct q6apm_graph * graph,uint32_t samples)302 int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
303 {
304 struct audioreach_module *module;
305
306 module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
307 if (!module)
308 return -ENODEV;
309
310 return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples);
311 }
312 EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence);
313
q6apm_enable_compress_module(struct device * dev,struct q6apm_graph * graph,bool en)314 int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en)
315 {
316 struct audioreach_module *module;
317
318 module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
319 if (!module)
320 return -ENODEV;
321
322 return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en);
323 }
324 EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);
325
q6apm_set_real_module_id(struct device * dev,struct q6apm_graph * graph,uint32_t codec_id)326 int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph,
327 uint32_t codec_id)
328 {
329 struct audioreach_module *module;
330 uint32_t module_id;
331
332 module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
333 if (!module)
334 return -ENODEV;
335
336 switch (codec_id) {
337 case SND_AUDIOCODEC_MP3:
338 module_id = MODULE_ID_MP3_DECODE;
339 break;
340 case SND_AUDIOCODEC_AAC:
341 module_id = MODULE_ID_AAC_DEC;
342 break;
343 case SND_AUDIOCODEC_FLAC:
344 module_id = MODULE_ID_FLAC_DEC;
345 break;
346 case SND_AUDIOCODEC_OPUS_RAW:
347 module_id = MODULE_ID_OPUS_DEC;
348 break;
349 default:
350 return -EINVAL;
351 }
352
353 return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID,
354 module_id);
355 }
356 EXPORT_SYMBOL_GPL(q6apm_set_real_module_id);
357
q6apm_graph_media_format_pcm(struct q6apm_graph * graph,struct audioreach_module_config * cfg)358 int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
359 {
360 struct audioreach_graph_info *info = graph->info;
361 struct audioreach_sub_graph *sgs;
362 struct audioreach_container *container;
363 struct audioreach_module *module;
364
365 list_for_each_entry(sgs, &info->sg_list, node) {
366 list_for_each_entry(container, &sgs->container_list, node) {
367 list_for_each_entry(module, &container->modules_list, node) {
368 if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||
369 (module->module_id == MODULE_ID_RD_SHARED_MEM_EP))
370 continue;
371
372 audioreach_set_media_format(graph, module, cfg);
373 }
374 }
375 }
376
377 return 0;
378
379 }
380 EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
381
q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph * graph)382 static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
383 {
384 struct audioreach_module *module;
385
386 module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
387 if (!module)
388 return -ENODEV;
389
390 return module->instance_id;
391
392 }
393
q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph * graph)394 int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
395 {
396 struct audioreach_module *module;
397
398 module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
399 if (!module)
400 return -ENODEV;
401
402 return module->instance_id;
403
404 }
405 EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
406
q6apm_write_async(struct q6apm_graph * graph,uint32_t len,uint32_t msw_ts,uint32_t lsw_ts,uint32_t wflags)407 int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
408 uint32_t lsw_ts, uint32_t wflags)
409 {
410 struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
411 struct audio_buffer *ab;
412 int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
413
414 struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer),
415 DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
416 graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
417 graph->port->id, iid);
418 if (IS_ERR(pkt))
419 return PTR_ERR(pkt);
420
421 write_buffer = (void *)pkt + GPR_HDR_SIZE;
422
423 mutex_lock(&graph->lock);
424 ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
425
426 write_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
427 write_buffer->buf_addr_msw = upper_32_bits(ab->phys);
428 write_buffer->buf_size = len;
429 write_buffer->timestamp_lsw = lsw_ts;
430 write_buffer->timestamp_msw = msw_ts;
431 write_buffer->mem_map_handle = graph->rx_data.mem_map_handle;
432 write_buffer->flags = wflags;
433
434 graph->rx_data.dsp_buf++;
435
436 if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
437 graph->rx_data.dsp_buf = 0;
438
439 mutex_unlock(&graph->lock);
440
441 return gpr_send_port_pkt(graph->port, pkt);
442 }
443 EXPORT_SYMBOL_GPL(q6apm_write_async);
444
q6apm_read(struct q6apm_graph * graph)445 int q6apm_read(struct q6apm_graph *graph)
446 {
447 struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;
448 struct audioreach_graph_data *port;
449 struct audio_buffer *ab;
450 int iid = q6apm_graph_get_tx_shmem_module_iid(graph);
451
452 struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer),
453 DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
454 graph->tx_data.dsp_buf, graph->port->id, iid);
455 if (IS_ERR(pkt))
456 return PTR_ERR(pkt);
457
458 read_buffer = (void *)pkt + GPR_HDR_SIZE;
459
460 mutex_lock(&graph->lock);
461 port = &graph->tx_data;
462 ab = &port->buf[port->dsp_buf];
463
464 read_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
465 read_buffer->buf_addr_msw = upper_32_bits(ab->phys);
466 read_buffer->mem_map_handle = port->mem_map_handle;
467 read_buffer->buf_size = ab->size;
468
469 port->dsp_buf++;
470
471 if (port->dsp_buf >= port->num_periods)
472 port->dsp_buf = 0;
473
474 mutex_unlock(&graph->lock);
475
476 return gpr_send_port_pkt(graph->port, pkt);
477 }
478 EXPORT_SYMBOL_GPL(q6apm_read);
479
q6apm_get_hw_pointer(struct q6apm_graph * graph,int dir)480 int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir)
481 {
482 struct audioreach_graph_data *data;
483
484 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
485 data = &graph->rx_data;
486 else
487 data = &graph->tx_data;
488
489 return (int)atomic_read(&data->hw_ptr);
490 }
491 EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer);
492
graph_callback(const struct gpr_resp_pkt * data,void * priv,int op)493 static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
494 {
495 struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
496 struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
497 struct apm_cmd_rsp_shared_mem_map_regions *rsp;
498 const struct gpr_ibasic_rsp_result_t *result;
499 struct q6apm_graph *graph = priv;
500 const struct gpr_hdr *hdr = &data->hdr;
501 struct device *dev = graph->dev;
502 uint32_t client_event;
503 phys_addr_t phys;
504 int token;
505
506 result = data->payload;
507
508 switch (hdr->opcode) {
509 case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
510 if (!graph->ar_graph)
511 break;
512 client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
513 mutex_lock(&graph->lock);
514 token = hdr->token & APM_WRITE_TOKEN_MASK;
515
516 done = data->payload;
517 phys = graph->rx_data.buf[token].phys;
518 mutex_unlock(&graph->lock);
519 /* token numbering starts at 0 */
520 atomic_set(&graph->rx_data.hw_ptr, token + 1);
521 if (lower_32_bits(phys) == done->buf_addr_lsw &&
522 upper_32_bits(phys) == done->buf_addr_msw) {
523 graph->result.opcode = hdr->opcode;
524 graph->result.status = done->status;
525 if (graph->cb)
526 graph->cb(client_event, hdr->token, data->payload, graph->priv);
527 } else {
528 dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
529 done->buf_addr_msw);
530 }
531
532 break;
533 case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
534 graph->result.opcode = hdr->opcode;
535 graph->result.status = 0;
536 rsp = data->payload;
537
538 if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
539 graph->rx_data.mem_map_handle = rsp->mem_map_handle;
540 else
541 graph->tx_data.mem_map_handle = rsp->mem_map_handle;
542
543 wake_up(&graph->cmd_wait);
544 break;
545 case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
546 if (!graph->ar_graph)
547 break;
548 client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
549 mutex_lock(&graph->lock);
550 rd_done = data->payload;
551 phys = graph->tx_data.buf[hdr->token].phys;
552 mutex_unlock(&graph->lock);
553 /* token numbering starts at 0 */
554 atomic_set(&graph->tx_data.hw_ptr, hdr->token + 1);
555
556 if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
557 lower_32_bits(phys) == rd_done->buf_addr_lsw) {
558 graph->result.opcode = hdr->opcode;
559 graph->result.status = rd_done->status;
560 if (graph->cb)
561 graph->cb(client_event, hdr->token, data->payload, graph->priv);
562 } else {
563 dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
564 rd_done->buf_addr_msw);
565 }
566 break;
567 case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
568 client_event = APM_CLIENT_EVENT_CMD_EOS_DONE;
569 if (graph->cb)
570 graph->cb(client_event, hdr->token, data->payload, graph->priv);
571 break;
572 case GPR_BASIC_RSP_RESULT:
573 switch (result->opcode) {
574 case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
575 graph->result.opcode = result->opcode;
576 graph->result.status = 0;
577 if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
578 graph->rx_data.mem_map_handle = 0;
579 else
580 graph->tx_data.mem_map_handle = 0;
581
582 wake_up(&graph->cmd_wait);
583 break;
584 case APM_CMD_SHARED_MEM_MAP_REGIONS:
585 case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
586 case APM_CMD_SET_CFG:
587 graph->result.opcode = result->opcode;
588 graph->result.status = result->status;
589 if (result->status)
590 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",
591 result->status, result->opcode);
592 wake_up(&graph->cmd_wait);
593 break;
594 default:
595 break;
596 }
597 break;
598 default:
599 break;
600 }
601 return 0;
602 }
603
q6apm_graph_open(struct device * dev,q6apm_cb cb,void * priv,int graph_id)604 struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
605 void *priv, int graph_id)
606 {
607 struct q6apm *apm = dev_get_drvdata(dev->parent);
608 struct audioreach_graph *ar_graph;
609 struct q6apm_graph *graph;
610 int ret;
611
612 ar_graph = q6apm_get_audioreach_graph(apm, graph_id);
613 if (IS_ERR(ar_graph)) {
614 dev_err(dev, "No graph found with id %d\n", graph_id);
615 return ERR_CAST(ar_graph);
616 }
617
618 graph = kzalloc_obj(*graph);
619 if (!graph) {
620 ret = -ENOMEM;
621 goto put_ar_graph;
622 }
623
624 graph->apm = apm;
625 graph->priv = priv;
626 graph->cb = cb;
627 graph->info = ar_graph->info;
628 graph->ar_graph = ar_graph;
629 graph->id = ar_graph->id;
630 graph->dev = dev;
631
632 mutex_init(&graph->lock);
633 init_waitqueue_head(&graph->cmd_wait);
634
635 graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);
636 if (IS_ERR(graph->port)) {
637 ret = PTR_ERR(graph->port);
638 goto free_graph;
639 }
640
641 return graph;
642
643 free_graph:
644 kfree(graph);
645 put_ar_graph:
646 kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
647 return ERR_PTR(ret);
648 }
649 EXPORT_SYMBOL_GPL(q6apm_graph_open);
650
q6apm_graph_close(struct q6apm_graph * graph)651 int q6apm_graph_close(struct q6apm_graph *graph)
652 {
653 struct audioreach_graph *ar_graph = graph->ar_graph;
654
655 graph->ar_graph = NULL;
656 kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
657 gpr_free_port(graph->port);
658 kfree(graph);
659
660 return 0;
661 }
662 EXPORT_SYMBOL_GPL(q6apm_graph_close);
663
q6apm_graph_prepare(struct q6apm_graph * graph)664 int q6apm_graph_prepare(struct q6apm_graph *graph)
665 {
666 return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
667 }
668 EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
669
q6apm_graph_start(struct q6apm_graph * graph)670 int q6apm_graph_start(struct q6apm_graph *graph)
671 {
672 struct audioreach_graph *ar_graph = graph->ar_graph;
673 int ret = 0;
674
675 if (ar_graph->start_count == 0)
676 ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);
677
678 ar_graph->start_count++;
679
680 return ret;
681 }
682 EXPORT_SYMBOL_GPL(q6apm_graph_start);
683
q6apm_graph_stop(struct q6apm_graph * graph)684 int q6apm_graph_stop(struct q6apm_graph *graph)
685 {
686 struct audioreach_graph *ar_graph = graph->ar_graph;
687
688 if (--ar_graph->start_count > 0)
689 return 0;
690
691 return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);
692 }
693 EXPORT_SYMBOL_GPL(q6apm_graph_stop);
694
q6apm_graph_flush(struct q6apm_graph * graph)695 int q6apm_graph_flush(struct q6apm_graph *graph)
696 {
697 return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
698 }
699 EXPORT_SYMBOL_GPL(q6apm_graph_flush);
700
q6apm_audio_probe(struct snd_soc_component * component)701 static int q6apm_audio_probe(struct snd_soc_component *component)
702 {
703 return audioreach_tplg_init(component);
704 }
705
q6apm_audio_remove(struct snd_soc_component * component)706 static void q6apm_audio_remove(struct snd_soc_component *component)
707 {
708 /* remove topology */
709 snd_soc_tplg_component_remove(component);
710 }
711
712 #define APM_AUDIO_DRV_NAME "q6apm-audio"
713
714 static const struct snd_soc_component_driver q6apm_audio_component = {
715 .name = APM_AUDIO_DRV_NAME,
716 .probe = q6apm_audio_probe,
717 .remove = q6apm_audio_remove,
718 };
719
apm_probe(gpr_device_t * gdev)720 static int apm_probe(gpr_device_t *gdev)
721 {
722 struct device *dev = &gdev->dev;
723 struct q6apm *apm;
724 int ret;
725
726 apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
727 if (!apm)
728 return -ENOMEM;
729
730 dev_set_drvdata(dev, apm);
731
732 mutex_init(&apm->lock);
733 apm->dev = dev;
734 apm->gdev = gdev;
735 init_waitqueue_head(&apm->wait);
736
737 INIT_LIST_HEAD(&apm->widget_list);
738 idr_init(&apm->graph_idr);
739 idr_init(&apm->graph_info_idr);
740 idr_init(&apm->sub_graphs_idr);
741 idr_init(&apm->containers_idr);
742
743 idr_init(&apm->modules_idr);
744
745 g_apm = apm;
746
747 q6apm_get_apm_state(apm);
748
749 ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
750 if (ret < 0) {
751 dev_err(dev, "failed to register q6apm: %d\n", ret);
752 return ret;
753 }
754
755 return of_platform_populate(dev->of_node, NULL, NULL, dev);
756 }
757
q6apm_find_module_by_mid(struct q6apm_graph * graph,uint32_t mid)758 struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
759 {
760 struct audioreach_graph_info *info = graph->info;
761 struct q6apm *apm = graph->apm;
762
763 return __q6apm_find_module_by_mid(apm, info, mid);
764
765 }
766
apm_callback(const struct gpr_resp_pkt * data,void * priv,int op)767 static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
768 {
769 gpr_device_t *gdev = priv;
770 struct q6apm *apm = dev_get_drvdata(&gdev->dev);
771 struct device *dev = &gdev->dev;
772 struct gpr_ibasic_rsp_result_t *result;
773 const struct gpr_hdr *hdr = &data->hdr;
774
775 result = data->payload;
776
777 switch (hdr->opcode) {
778 case APM_CMD_RSP_GET_SPF_STATE:
779 apm->result.opcode = hdr->opcode;
780 apm->result.status = 0;
781 /* First word of result it state */
782 apm->state = result->opcode;
783 wake_up(&apm->wait);
784 break;
785 case GPR_BASIC_RSP_RESULT:
786 switch (result->opcode) {
787 case APM_CMD_GRAPH_START:
788 case APM_CMD_GRAPH_OPEN:
789 case APM_CMD_GRAPH_PREPARE:
790 case APM_CMD_GRAPH_CLOSE:
791 case APM_CMD_GRAPH_FLUSH:
792 case APM_CMD_GRAPH_STOP:
793 case APM_CMD_SET_CFG:
794 apm->result.opcode = result->opcode;
795 apm->result.status = result->status;
796 if (result->status)
797 dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
798 result->opcode);
799 wake_up(&apm->wait);
800 break;
801 default:
802 break;
803 }
804 break;
805 default:
806 break;
807 }
808
809 return 0;
810 }
811
812 #ifdef CONFIG_OF
813 static const struct of_device_id apm_device_id[] = {
814 { .compatible = "qcom,q6apm" },
815 {},
816 };
817 MODULE_DEVICE_TABLE(of, apm_device_id);
818 #endif
819
820 static gpr_driver_t apm_driver = {
821 .probe = apm_probe,
822 .gpr_callback = apm_callback,
823 .driver = {
824 .name = "qcom-apm",
825 .of_match_table = of_match_ptr(apm_device_id),
826 },
827 };
828
829 module_gpr_driver(apm_driver);
830 MODULE_DESCRIPTION("Audio Process Manager");
831 MODULE_LICENSE("GPL");
832