brintos

brintos / linux-shallow public Read only

0
0
Text · 20.1 KiB · 2a2a5bd Raw
833 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2020, Linaro Limited3 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, 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 130static 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 149static 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 164bool q6apm_is_adsp_ready(void)165{166	if (g_apm)167		return q6apm_get_apm_state(g_apm);168 169	return false;170}171EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready);172 173static 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 193int 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	else201		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}211EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);212 213int 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	else224		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	else242		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}267EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);268 269int 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	else279		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}299EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);300 301int q6apm_remove_initial_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_INITIAL_SILENCE, samples);310}311EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence);312 313int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples)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_REMOVE_TRAILING_SILENCE, samples);322}323EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence);324 325int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en)326{327	struct audioreach_module *module;328 329	module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);330	if (!module)331		return -ENODEV;332 333	return audioreach_send_u32_param(graph, module, PARAM_ID_MODULE_ENABLE, en);334}335EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);336 337int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph,338			     uint32_t codec_id)339{340	struct audioreach_module *module;341	uint32_t module_id;342 343	module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER);344	if (!module)345		return -ENODEV;346 347	switch (codec_id) {348	case SND_AUDIOCODEC_MP3:349		module_id = MODULE_ID_MP3_DECODE;350		break;351	case SND_AUDIOCODEC_AAC:352		module_id = MODULE_ID_AAC_DEC;353		break;354	case SND_AUDIOCODEC_FLAC:355		module_id = MODULE_ID_FLAC_DEC;356		break;357	default:358		return -EINVAL;359	}360 361	return audioreach_send_u32_param(graph, module, PARAM_ID_REAL_MODULE_ID,362					 module_id);363}364EXPORT_SYMBOL_GPL(q6apm_set_real_module_id);365 366int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg)367{368	struct audioreach_graph_info *info = graph->info;369	struct audioreach_sub_graph *sgs;370	struct audioreach_container *container;371	struct audioreach_module *module;372 373	list_for_each_entry(sgs, &info->sg_list, node) {374		list_for_each_entry(container, &sgs->container_list, node) {375			list_for_each_entry(module, &container->modules_list, node) {376				if ((module->module_id == MODULE_ID_WR_SHARED_MEM_EP) ||377					(module->module_id == MODULE_ID_RD_SHARED_MEM_EP))378					continue;379 380				audioreach_set_media_format(graph, module, cfg);381			}382		}383	}384 385	return 0;386 387}388EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);389 390static int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)391{392	struct audioreach_module *module;393 394	module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP);395	if (!module)396		return -ENODEV;397 398	return module->instance_id;399 400}401 402int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)403{404	struct audioreach_module *module;405 406	module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP);407	if (!module)408		return -ENODEV;409 410	return module->instance_id;411 412}413EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);414 415int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,416		      uint32_t lsw_ts, uint32_t wflags)417{418	struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer;419	struct audio_buffer *ab;420	struct gpr_pkt *pkt;421	int rc, iid;422 423	iid = q6apm_graph_get_rx_shmem_module_iid(graph);424	pkt = audioreach_alloc_pkt(sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2,425				   graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT),426				   graph->port->id, iid);427	if (IS_ERR(pkt))428		return PTR_ERR(pkt);429 430	write_buffer = (void *)pkt + GPR_HDR_SIZE;431 432	mutex_lock(&graph->lock);433	ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];434 435	write_buffer->buf_addr_lsw = lower_32_bits(ab->phys);436	write_buffer->buf_addr_msw = upper_32_bits(ab->phys);437	write_buffer->buf_size = len;438	write_buffer->timestamp_lsw = lsw_ts;439	write_buffer->timestamp_msw = msw_ts;440	write_buffer->mem_map_handle = graph->rx_data.mem_map_handle;441	write_buffer->flags = wflags;442 443	graph->rx_data.dsp_buf++;444 445	if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)446		graph->rx_data.dsp_buf = 0;447 448	mutex_unlock(&graph->lock);449 450	rc = gpr_send_port_pkt(graph->port, pkt);451 452	kfree(pkt);453 454	return rc;455}456EXPORT_SYMBOL_GPL(q6apm_write_async);457 458int q6apm_read(struct q6apm_graph *graph)459{460	struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer;461	struct audioreach_graph_data *port;462	struct audio_buffer *ab;463	struct gpr_pkt *pkt;464	int rc, iid;465 466	iid = q6apm_graph_get_tx_shmem_module_iid(graph);467	pkt = audioreach_alloc_pkt(sizeof(*read_buffer), DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2,468				   graph->tx_data.dsp_buf, graph->port->id, iid);469	if (IS_ERR(pkt))470		return PTR_ERR(pkt);471 472	read_buffer = (void *)pkt + GPR_HDR_SIZE;473 474	mutex_lock(&graph->lock);475	port = &graph->tx_data;476	ab = &port->buf[port->dsp_buf];477 478	read_buffer->buf_addr_lsw = lower_32_bits(ab->phys);479	read_buffer->buf_addr_msw = upper_32_bits(ab->phys);480	read_buffer->mem_map_handle = port->mem_map_handle;481	read_buffer->buf_size = ab->size;482 483	port->dsp_buf++;484 485	if (port->dsp_buf >= port->num_periods)486		port->dsp_buf = 0;487 488	mutex_unlock(&graph->lock);489 490	rc = gpr_send_port_pkt(graph->port, pkt);491	kfree(pkt);492 493	return rc;494}495EXPORT_SYMBOL_GPL(q6apm_read);496 497static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)498{499	struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;500	struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;501	struct apm_cmd_rsp_shared_mem_map_regions *rsp;502	struct gpr_ibasic_rsp_result_t *result;503	struct q6apm_graph *graph = priv;504	struct gpr_hdr *hdr = &data->hdr;505	struct device *dev = graph->dev;506	uint32_t client_event;507	phys_addr_t phys;508	int token;509 510	result = data->payload;511 512	switch (hdr->opcode) {513	case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:514		if (!graph->ar_graph)515			break;516		client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;517		mutex_lock(&graph->lock);518		token = hdr->token & APM_WRITE_TOKEN_MASK;519 520		done = data->payload;521		phys = graph->rx_data.buf[token].phys;522		mutex_unlock(&graph->lock);523 524		if (lower_32_bits(phys) == done->buf_addr_lsw &&525		    upper_32_bits(phys) == done->buf_addr_msw) {526			graph->result.opcode = hdr->opcode;527			graph->result.status = done->status;528			if (graph->cb)529				graph->cb(client_event, hdr->token, data->payload, graph->priv);530		} else {531			dev_err(dev, "WR BUFF Unexpected addr %08x-%08x\n", done->buf_addr_lsw,532				done->buf_addr_msw);533		}534 535		break;536	case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:537		graph->result.opcode = hdr->opcode;538		graph->result.status = 0;539		rsp = data->payload;540 541		if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)542			graph->rx_data.mem_map_handle = rsp->mem_map_handle;543		else544			graph->tx_data.mem_map_handle = rsp->mem_map_handle;545 546		wake_up(&graph->cmd_wait);547		break;548	case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:549		if (!graph->ar_graph)550			break;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		client_event = APM_CLIENT_EVENT_CMD_EOS_DONE;570		if (graph->cb)571			graph->cb(client_event, hdr->token, data->payload, graph->priv);572		break;573	case GPR_BASIC_RSP_RESULT:574		switch (result->opcode) {575		case APM_CMD_SHARED_MEM_UNMAP_REGIONS:576			graph->result.opcode = result->opcode;577			graph->result.status = 0;578			if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)579				graph->rx_data.mem_map_handle = 0;580			else581				graph->tx_data.mem_map_handle = 0;582 583			wake_up(&graph->cmd_wait);584			break;585		case APM_CMD_SHARED_MEM_MAP_REGIONS:586		case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:587		case APM_CMD_SET_CFG:588			graph->result.opcode = result->opcode;589			graph->result.status = result->status;590			if (result->status)591				dev_err(dev, "Error (%d) Processing 0x%08x cmd\n",592					result->status, result->opcode);593			wake_up(&graph->cmd_wait);594			break;595		default:596			break;597		}598		break;599	default:600		break;601	}602	return 0;603}604 605struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,606				     void *priv, int graph_id)607{608	struct q6apm *apm = dev_get_drvdata(dev->parent);609	struct audioreach_graph *ar_graph;610	struct q6apm_graph *graph;611	int ret;612 613	ar_graph = q6apm_get_audioreach_graph(apm, graph_id);614	if (IS_ERR(ar_graph)) {615		dev_err(dev, "No graph found with id %d\n", graph_id);616		return ERR_CAST(ar_graph);617	}618 619	graph = kzalloc(sizeof(*graph), GFP_KERNEL);620	if (!graph) {621		ret = -ENOMEM;622		goto put_ar_graph;623	}624 625	graph->apm = apm;626	graph->priv = priv;627	graph->cb = cb;628	graph->info = ar_graph->info;629	graph->ar_graph = ar_graph;630	graph->id = ar_graph->id;631	graph->dev = dev;632 633	mutex_init(&graph->lock);634	init_waitqueue_head(&graph->cmd_wait);635 636	graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph);637	if (IS_ERR(graph->port)) {638		ret = PTR_ERR(graph->port);639		goto free_graph;640	}641 642	return graph;643 644free_graph:645	kfree(graph);646put_ar_graph:647	kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);648	return ERR_PTR(ret);649}650EXPORT_SYMBOL_GPL(q6apm_graph_open);651 652int q6apm_graph_close(struct q6apm_graph *graph)653{654	struct audioreach_graph *ar_graph = graph->ar_graph;655 656	graph->ar_graph = NULL;657	kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);658	gpr_free_port(graph->port);659	kfree(graph);660 661	return 0;662}663EXPORT_SYMBOL_GPL(q6apm_graph_close);664 665int q6apm_graph_prepare(struct q6apm_graph *graph)666{667	return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);668}669EXPORT_SYMBOL_GPL(q6apm_graph_prepare);670 671int q6apm_graph_start(struct q6apm_graph *graph)672{673	struct audioreach_graph *ar_graph = graph->ar_graph;674	int ret = 0;675 676	if (ar_graph->start_count == 0)677		ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START);678 679	ar_graph->start_count++;680 681	return ret;682}683EXPORT_SYMBOL_GPL(q6apm_graph_start);684 685int q6apm_graph_stop(struct q6apm_graph *graph)686{687	struct audioreach_graph *ar_graph = graph->ar_graph;688 689	if (--ar_graph->start_count > 0)690		return 0;691 692	return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP);693}694EXPORT_SYMBOL_GPL(q6apm_graph_stop);695 696int q6apm_graph_flush(struct q6apm_graph *graph)697{698	return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);699}700EXPORT_SYMBOL_GPL(q6apm_graph_flush);701 702static int q6apm_audio_probe(struct snd_soc_component *component)703{704	return audioreach_tplg_init(component);705}706 707static void q6apm_audio_remove(struct snd_soc_component *component)708{709	/* remove topology */710	snd_soc_tplg_component_remove(component);711}712 713#define APM_AUDIO_DRV_NAME "q6apm-audio"714 715static const struct snd_soc_component_driver q6apm_audio_component = {716	.name		= APM_AUDIO_DRV_NAME,717	.probe		= q6apm_audio_probe,718	.remove		= q6apm_audio_remove,719};720 721static int apm_probe(gpr_device_t *gdev)722{723	struct device *dev = &gdev->dev;724	struct q6apm *apm;725	int ret;726 727	apm = devm_kzalloc(dev, sizeof(*apm), GFP_KERNEL);728	if (!apm)729		return -ENOMEM;730 731	dev_set_drvdata(dev, apm);732 733	mutex_init(&apm->lock);734	apm->dev = dev;735	apm->gdev = gdev;736	init_waitqueue_head(&apm->wait);737 738	INIT_LIST_HEAD(&apm->widget_list);739	idr_init(&apm->graph_idr);740	idr_init(&apm->graph_info_idr);741	idr_init(&apm->sub_graphs_idr);742	idr_init(&apm->containers_idr);743 744	idr_init(&apm->modules_idr);745 746	g_apm = apm;747 748	q6apm_get_apm_state(apm);749 750	ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);751	if (ret < 0) {752		dev_err(dev, "failed to register q6apm: %d\n", ret);753		return ret;754	}755 756	return of_platform_populate(dev->of_node, NULL, NULL, dev);757}758 759struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid)760{761	struct audioreach_graph_info *info = graph->info;762	struct q6apm *apm = graph->apm;763 764	return __q6apm_find_module_by_mid(apm, info, mid);765 766}767 768static int apm_callback(struct gpr_resp_pkt *data, void *priv, int op)769{770	gpr_device_t *gdev = priv;771	struct q6apm *apm = dev_get_drvdata(&gdev->dev);772	struct device *dev = &gdev->dev;773	struct gpr_ibasic_rsp_result_t *result;774	struct gpr_hdr *hdr = &data->hdr;775 776	result = data->payload;777 778	switch (hdr->opcode) {779	case APM_CMD_RSP_GET_SPF_STATE:780		apm->result.opcode = hdr->opcode;781		apm->result.status = 0;782		/* First word of result it state */783		apm->state = result->opcode;784		wake_up(&apm->wait);785		break;786	case GPR_BASIC_RSP_RESULT:787		switch (result->opcode) {788		case APM_CMD_GRAPH_START:789		case APM_CMD_GRAPH_OPEN:790		case APM_CMD_GRAPH_PREPARE:791		case APM_CMD_GRAPH_CLOSE:792		case APM_CMD_GRAPH_FLUSH:793		case APM_CMD_GRAPH_STOP:794		case APM_CMD_SET_CFG:795			apm->result.opcode = result->opcode;796			apm->result.status = result->status;797			if (result->status)798				dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,799					result->opcode);800			wake_up(&apm->wait);801			break;802		default:803			break;804		}805		break;806	default:807		break;808	}809 810	return 0;811}812 813#ifdef CONFIG_OF814static const struct of_device_id apm_device_id[]  = {815	{ .compatible = "qcom,q6apm" },816	{},817};818MODULE_DEVICE_TABLE(of, apm_device_id);819#endif820 821static gpr_driver_t apm_driver = {822	.probe = apm_probe,823	.gpr_callback = apm_callback,824	.driver = {825		.name = "qcom-apm",826		.of_match_table = of_match_ptr(apm_device_id),827	},828};829 830module_gpr_driver(apm_driver);831MODULE_DESCRIPTION("Audio Process Manager");832MODULE_LICENSE("GPL");833