FILE
q6apm.c
File preview with syntax highlighting.
linux-6.19.14/sound/soc/qcom/qdsp6/q6apm.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.