cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

q6apm.c (20226B)


      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
     30int 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
     38static 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
     91static 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
    127static 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
    145static 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
    160static 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
    180static 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
    198static 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
    216bool 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
    236int 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
    261int 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}
    279EXPORT_SYMBOL_GPL(q6apm_graph_media_format_shmem);
    280
    281int 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}
    335EXPORT_SYMBOL_GPL(q6apm_map_memory_regions);
    336
    337int 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}
    367EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
    368
    369int 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}
    391EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm);
    392
    393static 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
    405int 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}
    416EXPORT_SYMBOL_GPL(q6apm_graph_get_rx_shmem_module_iid);
    417
    418int 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}
    459EXPORT_SYMBOL_GPL(q6apm_write_async);
    460
    461int 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}
    498EXPORT_SYMBOL_GPL(q6apm_read);
    499
    500static 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
    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	gpr_free_port(graph->port);
    653	kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
    654	kfree(graph);
    655
    656	return 0;
    657}
    658EXPORT_SYMBOL_GPL(q6apm_graph_close);
    659
    660int q6apm_graph_prepare(struct q6apm_graph *graph)
    661{
    662	return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE);
    663}
    664EXPORT_SYMBOL_GPL(q6apm_graph_prepare);
    665
    666int 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}
    678EXPORT_SYMBOL_GPL(q6apm_graph_start);
    679
    680int 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}
    689EXPORT_SYMBOL_GPL(q6apm_graph_stop);
    690
    691int q6apm_graph_flush(struct q6apm_graph *graph)
    692{
    693	return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH);
    694}
    695EXPORT_SYMBOL_GPL(q6apm_graph_flush);
    696
    697static int q6apm_audio_probe(struct snd_soc_component *component)
    698{
    699	return audioreach_tplg_init(component);
    700}
    701
    702static 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
    710static 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
    716static 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
    751struct 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
    760static 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
    806static const struct of_device_id apm_device_id[]  = {
    807	{ .compatible = "qcom,q6apm" },
    808	{},
    809};
    810MODULE_DEVICE_TABLE(of, apm_device_id);
    811#endif
    812
    813static 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
    822module_gpr_driver(apm_driver);
    823MODULE_DESCRIPTION("Audio Process Manager");
    824MODULE_LICENSE("GPL");