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