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