Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/qcom/qdsp6/q6apm.c
53558 views
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
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
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(sizeof(*graph), GFP_KERNEL);
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
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
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
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
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
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
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
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 = kcalloc(periods, sizeof(struct audio_buffer), GFP_KERNEL);
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
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
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
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
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
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
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
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
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
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
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
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
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
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(sizeof(*graph), GFP_KERNEL);
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
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
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
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
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
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
701
static int q6apm_audio_probe(struct snd_soc_component *component)
702
{
703
return audioreach_tplg_init(component);
704
}
705
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
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
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
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
833