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