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