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