File Index
Files and directories
Sign in
FILE

q6apm.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/qcom/qdsp6/q6apm.c C
  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 */
 22struct 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
 30static struct q6apm *g_apm;
 31
 32int 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
 40static 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
 94static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode)
 95{
 96	struct audioreach_graph_info *info = graph->info;
 97	int num_sub_graphs = info->num_sub_graphs;
 98	struct apm_module_param_data *param_data;
 99	struct apm_graph_mgmt_cmd *mgmt_cmd;
100	struct audioreach_sub_graph *sg;
101	struct q6apm *apm = graph->apm;
102	int i = 0, payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs);
103	struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, 0);
104	if (IS_ERR(pkt))
105		return PTR_ERR(pkt);
106
107	mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
108
109	mgmt_cmd->num_sub_graphs = num_sub_graphs;
110
111	param_data = &mgmt_cmd->param_data;
112	param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
113	param_data->param_id = APM_PARAM_ID_SUB_GRAPH_LIST;
114	param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
115
116	list_for_each_entry(sg, &info->sg_list, node)
117		mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id;
118
119	return q6apm_send_cmd_sync(apm, pkt, 0);
120}
121
122static void q6apm_put_audioreach_graph(struct kref *ref)
123{
124	struct audioreach_graph *graph;
125	struct q6apm *apm;
126
127	graph = container_of(ref, struct audioreach_graph, refcount);
128	apm = graph->apm;
129
130	audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE);
131
132	mutex_lock(&apm->lock);
133	graph = idr_remove(&apm->graph_idr, graph->id);
134	mutex_unlock(&apm->lock);
135
136	kfree(graph->graph);
137	kfree(graph);
138}
139
140
141static int q6apm_get_apm_state(struct q6apm *apm)
142{
143	struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(0,
144								APM_CMD_GET_SPF_STATE, 0);
145	if (IS_ERR(pkt))
146		return PTR_ERR(pkt);
147
148	q6apm_send_cmd_sync(apm, pkt, APM_CMD_RSP_GET_SPF_STATE);
149
150	return apm->state;
151}
152
153bool q6apm_is_adsp_ready(void)
154{
155	if (g_apm)
156		return q6apm_get_apm_state(g_apm);
157
158	return false;
159}
160EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready);
161
162static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
163						    struct audioreach_graph_info *info,
164						    uint32_t mid)
165{
166	struct audioreach_container *container;
167	struct audioreach_sub_graph *sgs;
168	struct audioreach_module *module;
169
170	list_for_each_entry(sgs, &info->sg_list, node) {
171		list_for_each_entry(container, &sgs->container_list, node) {
172			list_for_each_entry(module, &container->modules_list, node) {
173				if (mid == module->module_id)
174					return module;
175			}
176		}
177	}
178
179	return NULL;
180}
181
182int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
183				   struct audioreach_module_config *cfg)
184{
185	struct audioreach_module *module;
186
187	if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE)
188		module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
189	else
190		module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
191
192	if (!module)
193		return -ENODEV;
194
195	audioreach_set_media_format(graph, module, cfg);
196
197	return 0;
198
199}
200EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
201
202int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys,
203			     size_t period_sz, unsigned int periods)
204{
205	struct audioreach_graph_data *data;
206	struct audio_buffer *buf;
207	int cnt;
208	int rc;
209
210	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
211		data = &graph->rx_data;
212	else
213		data = &graph->tx_data;
214
215	mutex_lock(&graph->lock);
216
217	if (data->buf) {
218		mutex_unlock(&graph->lock);
219		return 0;
220	}
221
222	buf = kcalloc(periods, sizeof(struct audio_buffer), GFP_KERNEL);
223	if (!buf) {
224		mutex_unlock(&graph->lock);
225		return -ENOMEM;
226	}
227
228	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
229		data = &graph->rx_data;
230	else
231		data = &graph->tx_data;
232
233	data->buf = buf;
234
235	buf[0].phys = phys;
236	buf[0].size = period_sz;
237
238	for (cnt = 1; cnt < periods; cnt++) {
239		if (period_sz > 0) {
240			buf[cnt].phys = buf[0].phys + (cnt * period_sz);
241			buf[cnt].size = period_sz;
242		}
243	}
244	data->num_periods = periods;
245
246	mutex_unlock(&graph->lock);
247
248	rc = audioreach_map_memory_regions(graph, dir, period_sz, periods, 1);
249	if (rc < 0) {
250		dev_err(graph->dev, "Memory_map_regions failed\n");
251		audioreach_graph_free_buf(graph);
252	}
253
254	return rc;
255}
256EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
257
258int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir)
259{
260	struct apm_cmd_shared_mem_unmap_regions *cmd;
261	struct audioreach_graph_data *data;
262	struct gpr_pkt *pkt __free(kfree) = NULL;
263	int rc;
264
265	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
266		data = &graph->rx_data;
267	else
268		data = &graph->tx_data;
269
270	if (!data->mem_map_handle)
271		return 0;
272
273	pkt = audioreach_alloc_apm_pkt(sizeof(*cmd), APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
274				     graph->port->id);
275	if (IS_ERR(pkt))
276		return PTR_ERR(pkt);
277
278	cmd = (void *)pkt + GPR_HDR_SIZE;
279	cmd->mem_map_handle = data->mem_map_handle;
280
281	rc = audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
282
283	audioreach_graph_free_buf(graph);
284
285	return rc;
286}
287EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
288
289int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
290{
291	struct audioreach_module *module;
292
293	module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
294	if (!module)
295		return -ENODEV;
296
297	return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_INITIAL_SILENCE, samples);
298}
299EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence);
300
301int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)
302{
303	struct audioreach_module *module;
304
305	module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
306	if (!module)
307		return -ENODEV;
308
309	return audioreach_send_u32_param(graph, module, PARAM_ID_REMOVE_TRAILING_SILENCE, samples);
310}
311EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence);
312
313int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en)
314{
315	struct audioreach_module *module;
316
317	module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
318	if (!module)
319		return -ENODEV;
320
321	return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en);
322}
323EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);
324
325int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph,
326			     uint32_t codec_id)
327{
328	struct audioreach_module *module;
329	uint32_t module_id;
330
331	module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);
332	if (!module)
333		return -ENODEV;
334
335	switch (codec_id) {
336	case SND_AUDIOCODEC_MP3:
337		module_id = MODULE_ID_MP3_DECODE;
338		break;
339	case SND_AUDIOCODEC_AAC:
340		module_id = MODULE_ID_AAC_DEC;
341		break;
342	case SND_AUDIOCODEC_FLAC:
343		module_id = MODULE_ID_FLAC_DEC;
344		break;
345	case SND_AUDIOCODEC_OPUS_RAW:
346		module_id = MODULE_ID_OPUS_DEC;
347		break;
348	default:
349		return -EINVAL;
350	}
351
352	return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID,
353					 module_id);
354}
355EXPORT_SYMBOL_GPL(q6apm_set_real_module_id);
356
357int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)
358{
359	struct audioreach_graph_info *info = graph->info;
360	struct audioreach_sub_graph *sgs;
361	struct audioreach_container *container;
362	struct audioreach_module *module;
363
364	list_for_each_entry(sgs, &info->sg_list, node) {
365		list_for_each_entry(container, &sgs->container_list, node) {
366			list_for_each_entry(module, &container->modules_list, node) {
367				if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||
368					(module->module_id == MODULE_ID_RD_SHARED_MEM_EP))
369					continue;
370
371				audioreach_set_media_format(graph, module, cfg);
372			}
373		}
374	}
375
376	return 0;
377
378}
379EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
380
381static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
382{
383	struct audioreach_module *module;
384
385	module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);
386	if (!module)
387		return -ENODEV;
388
389	return module->instance_id;
390
391}
392
393int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
394{
395	struct audioreach_module *module;
396
397	module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);
398	if (!module)
399		return -ENODEV;
400
401	return module->instance_id;
402
403}
404EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
405
406int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
407		      uint32_t lsw_ts, uint32_t wflags)
408{
409	struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;
410	struct audio_buffer *ab;
411	int iid = q6apm_graph_get_rx_shmem_module_iid(graph);
412	struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer),
413					DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,
414					graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),
415					graph->port->id, iid);
416	if (IS_ERR(pkt))
417		return PTR_ERR(pkt);
418
419	write_buffer = (void *)pkt + GPR_HDR_SIZE;
420
421	mutex_lock(&graph->lock);
422	ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
423
424	write_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
425	write_buffer->buf_addr_msw = upper_32_bits(ab->phys);
426	write_buffer->buf_size = len;
427	write_buffer->timestamp_lsw = lsw_ts;
428	write_buffer->timestamp_msw = msw_ts;
429	write_buffer->mem_map_handle = graph->rx_data.mem_map_handle;
430	write_buffer->flags = wflags;
431
432	graph->rx_data.dsp_buf++;
433
434	if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
435		graph->rx_data.dsp_buf = 0;
436
437	mutex_unlock(&graph->lock);
438
439	return gpr_send_port_pkt(graph->port, pkt);
440}
441EXPORT_SYMBOL_GPL(q6apm_write_async);
442
443int q6apm_read(struct q6apm_graph *graph)
444{
445	struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;
446	struct audioreach_graph_data *port;
447	struct audio_buffer *ab;
448	int iid = q6apm_graph_get_tx_shmem_module_iid(graph);
449	struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer),
450					DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,
451					graph->tx_data.dsp_buf, graph->port->id, iid);
452	if (IS_ERR(pkt))
453		return PTR_ERR(pkt);
454
455	read_buffer = (void *)pkt + GPR_HDR_SIZE;
456
457	mutex_lock(&graph->lock);
458	port = &graph->tx_data;
459	ab = &port->buf[port->dsp_buf];
460
461	read_buffer->buf_addr_lsw = lower_32_bits(ab->phys);
462	read_buffer->buf_addr_msw = upper_32_bits(ab->phys);
463	read_buffer->mem_map_handle = port->mem_map_handle;
464	read_buffer->buf_size = ab->size;
465
466	port->dsp_buf++;
467
468	if (port->dsp_buf >= port->num_periods)
469		port->dsp_buf = 0;
470
471	mutex_unlock(&graph->lock);
472
473	return gpr_send_port_pkt(graph->port, pkt);
474}
475EXPORT_SYMBOL_GPL(q6apm_read);
476
477int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir)
478{
479	struct audioreach_graph_data *data;
480
481	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
482		data = &graph->rx_data;
483	else
484		data = &graph->tx_data;
485
486	return (int)atomic_read(&data->hw_ptr);
487}
488EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer);
489
490static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
491{
492	struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
493	struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
494	struct apm_cmd_rsp_shared_mem_map_regions *rsp;
495	struct gpr_ibasic_rsp_result_t *result;
496	struct q6apm_graph *graph = priv;
497	struct gpr_hdr *hdr = &data->hdr;
498	struct device *dev = graph->dev;
499	uint32_t client_event;
500	phys_addr_t phys;
501	int token;
502
503	result = data->payload;
504
505	switch (hdr->opcode) {
506	case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
507		if (!graph->ar_graph)
508			break;
509		client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
510		mutex_lock(&graph->lock);
511		token = hdr->token & APM_WRITE_TOKEN_MASK;
512
513		done = data->payload;
514		phys = graph->rx_data.buf[token].phys;
515		mutex_unlock(&graph->lock);
516		/* token numbering starts at 0 */
517		atomic_set(&graph->rx_data.hw_ptr, token + 1);
518		if (lower_32_bits(phys) == done->buf_addr_lsw &&
519		    upper_32_bits(phys) == done->buf_addr_msw) {
520			graph->result.opcode = hdr->opcode;
521			graph->result.status = done->status;
522			if (graph->cb)
523				graph->cb(client_event, hdr->token, data->payload, graph->priv);
524		} else {
525			dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,
526				done->buf_addr_msw);
527		}
528
529		break;
530	case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
531		graph->result.opcode = hdr->opcode;
532		graph->result.status = 0;
533		rsp = data->payload;
534
535		if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
536			graph->rx_data.mem_map_handle = rsp->mem_map_handle;
537		else
538			graph->tx_data.mem_map_handle = rsp->mem_map_handle;
539
540		wake_up(&graph->cmd_wait);
541		break;
542	case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
543		if (!graph->ar_graph)
544			break;
545		client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
546		mutex_lock(&graph->lock);
547		rd_done = data->payload;
548		phys = graph->tx_data.buf[hdr->token].phys;
549		mutex_unlock(&graph->lock);
550		/* token numbering starts at 0 */
551		atomic_set(&graph->tx_data.hw_ptr, hdr->token + 1);
552
553		if (upper_32_bits(phys) == rd_done->buf_addr_msw &&
554		    lower_32_bits(phys) == rd_done->buf_addr_lsw) {
555			graph->result.opcode = hdr->opcode;
556			graph->result.status = rd_done->status;
557			if (graph->cb)
558				graph->cb(client_event, hdr->token, data->payload, graph->priv);
559		} else {
560			dev_err(dev, "RD BUFF Unexpected addr %08x-%08x\n", rd_done->buf_addr_lsw,
561				rd_done->buf_addr_msw);
562		}
563		break;
564	case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
565		client_event = APM_CLIENT_EVENT_CMD_EOS_DONE;
566		if (graph->cb)
567			graph->cb(client_event, hdr->token, data->payload, graph->priv);
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
601struct 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
640free_graph:
641	kfree(graph);
642put_ar_graph:
643	kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
644	return ERR_PTR(ret);
645}
646EXPORT_SYMBOL_GPL(q6apm_graph_open);
647
648int q6apm_graph_close(struct q6apm_graph *graph)
649{
650	struct audioreach_graph *ar_graph = graph->ar_graph;
651
652	graph->ar_graph = NULL;
653	kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
654	gpr_free_port(graph->port);
655	kfree(graph);
656
657	return 0;
658}
659EXPORT_SYMBOL_GPL(q6apm_graph_close);
660
661int q6apm_graph_prepare(struct q6apm_graph *graph)
662{
663	return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
664}
665EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
666
667int 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}
679EXPORT_SYMBOL_GPL(q6apm_graph_start);
680
681int 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}
690EXPORT_SYMBOL_GPL(q6apm_graph_stop);
691
692int q6apm_graph_flush(struct q6apm_graph *graph)
693{
694	return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
695}
696EXPORT_SYMBOL_GPL(q6apm_graph_flush);
697
698static int q6apm_audio_probe(struct snd_soc_component *component)
699{
700	return audioreach_tplg_init(component);
701}
702
703static 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
711static 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	.remove_order   = SND_SOC_COMP_ORDER_LAST,
716};
717
718static int apm_probe(gpr_device_t *gdev)
719{
720	struct device *dev = &gdev->dev;
721	struct q6apm *apm;
722	int ret;
723
724	apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);
725	if (!apm)
726		return -ENOMEM;
727
728	dev_set_drvdata(dev, apm);
729
730	mutex_init(&apm->lock);
731	apm->dev = dev;
732	apm->gdev = gdev;
733	init_waitqueue_head(&apm->wait);
734
735	INIT_LIST_HEAD(&apm->widget_list);
736	idr_init(&apm->graph_idr);
737	idr_init(&apm->graph_info_idr);
738	idr_init(&apm->sub_graphs_idr);
739	idr_init(&apm->containers_idr);
740
741	idr_init(&apm->modules_idr);
742
743	g_apm = apm;
744
745	q6apm_get_apm_state(apm);
746
747	ret = snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
748	if (ret < 0) {
749		dev_err(dev, "failed to register q6apm: %d\n", ret);
750		return ret;
751	}
752
753	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
754	if (ret)
755		snd_soc_unregister_component(dev);
756
757	return ret;
758}
759
760static void apm_remove(gpr_device_t *gdev)
761{
762	snd_soc_unregister_component(&gdev->dev);
763}
764
765struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)
766{
767	struct audioreach_graph_info *info = graph->info;
768	struct q6apm *apm = graph->apm;
769
770	return __q6apm_find_module_by_mid(apm, info, mid);
771
772}
773
774static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)
775{
776	gpr_device_t *gdev = priv;
777	struct q6apm *apm = dev_get_drvdata(&gdev->dev);
778	struct device *dev = &gdev->dev;
779	struct gpr_ibasic_rsp_result_t *result;
780	struct gpr_hdr *hdr = &data->hdr;
781
782	result = data->payload;
783
784	switch (hdr->opcode) {
785	case APM_CMD_RSP_GET_SPF_STATE:
786		apm->result.opcode = hdr->opcode;
787		apm->result.status = 0;
788		/* First word of result it state */
789		apm->state = result->opcode;
790		wake_up(&apm->wait);
791		break;
792	case GPR_BASIC_RSP_RESULT:
793		switch (result->opcode) {
794		case APM_CMD_GRAPH_START:
795		case APM_CMD_GRAPH_OPEN:
796		case APM_CMD_GRAPH_PREPARE:
797		case APM_CMD_GRAPH_CLOSE:
798		case APM_CMD_GRAPH_FLUSH:
799		case APM_CMD_GRAPH_STOP:
800		case APM_CMD_SET_CFG:
801			apm->result.opcode = result->opcode;
802			apm->result.status = result->status;
803			if (result->status)
804				dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
805					result->opcode);
806			wake_up(&apm->wait);
807			break;
808		default:
809			break;
810		}
811		break;
812	default:
813		break;
814	}
815
816	return 0;
817}
818
819#ifdef CONFIG_OF
820static const struct of_device_id apm_device_id[]  = {
821	{ .compatible = "qcom,q6apm" },
822	{},
823};
824MODULE_DEVICE_TABLE(of, apm_device_id);
825#endif
826
827static gpr_driver_t apm_driver = {
828	.probe = apm_probe,
829	.remove = apm_remove,
830	.gpr_callback = apm_callback,
831	.driver = {
832		.name = "qcom-apm",
833		.of_match_table = of_match_ptr(apm_device_id),
834	},
835};
836
837module_gpr_driver(apm_driver);
838MODULE_DESCRIPTION("Audio Process Manager");
839MODULE_LICENSE("GPL");
~/ Read-only file preview.