Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v6.1 750 lines 20 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2// 3// Copyright(c) 2019-2022 Intel Corporation. All rights reserved. 4// 5// Author: Cezary Rojewski <cezary.rojewski@intel.com> 6// 7// SOF client support: 8// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 9// Peter Ujfalusi <peter.ujfalusi@linux.intel.com> 10// 11 12#include <linux/debugfs.h> 13#include <linux/module.h> 14#include <linux/pm_runtime.h> 15#include <linux/string_helpers.h> 16 17#include <sound/soc.h> 18#include <sound/sof/header.h> 19#include "sof-client.h" 20#include "sof-client-probes.h" 21 22#define SOF_PROBES_SUSPEND_DELAY_MS 3000 23/* only extraction supported for now */ 24#define SOF_PROBES_NUM_DAI_LINKS 1 25 26#define SOF_PROBES_INVALID_NODE_ID UINT_MAX 27 28static bool __read_mostly sof_probes_enabled; 29module_param_named(enable, sof_probes_enabled, bool, 0444); 30MODULE_PARM_DESC(enable, "Enable SOF probes support"); 31 32struct sof_probes_priv { 33 struct dentry *dfs_points; 34 struct dentry *dfs_points_remove; 35 u32 extractor_stream_tag; 36 struct snd_soc_card card; 37 38 const struct sof_probes_host_ops *host_ops; 39}; 40 41struct sof_probe_point_desc { 42 unsigned int buffer_id; 43 unsigned int purpose; 44 unsigned int stream_tag; 45} __packed; 46 47struct sof_probe_dma { 48 unsigned int stream_tag; 49 unsigned int dma_buffer_size; 50} __packed; 51 52struct sof_ipc_probe_dma_add_params { 53 struct sof_ipc_cmd_hdr hdr; 54 unsigned int num_elems; 55 struct sof_probe_dma dma[]; 56} __packed; 57 58struct sof_ipc_probe_info_params { 59 struct sof_ipc_reply rhdr; 60 unsigned int num_elems; 61 union { 62 struct sof_probe_dma dma[0]; 63 struct sof_probe_point_desc desc[0]; 64 }; 65} __packed; 66 67struct sof_ipc_probe_point_add_params { 68 struct sof_ipc_cmd_hdr hdr; 69 unsigned int num_elems; 70 struct sof_probe_point_desc desc[]; 71} __packed; 72 73struct sof_ipc_probe_point_remove_params { 74 struct sof_ipc_cmd_hdr hdr; 75 unsigned int num_elems; 76 unsigned int buffer_id[]; 77} __packed; 78 79/** 80 * sof_probes_init - initialize data probing 81 * @cdev: SOF client device 82 * @stream_tag: Extractor stream tag 83 * @buffer_size: DMA buffer size to set for extractor 84 * 85 * Host chooses whether extraction is supported or not by providing 86 * valid stream tag to DSP. Once specified, stream described by that 87 * tag will be tied to DSP for extraction for the entire lifetime of 88 * probe. 89 * 90 * Probing is initialized only once and each INIT request must be 91 * matched by DEINIT call. 92 */ 93static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag, 94 size_t buffer_size) 95{ 96 struct sof_ipc_probe_dma_add_params *msg; 97 size_t size = struct_size(msg, dma, 1); 98 struct sof_ipc_reply reply; 99 int ret; 100 101 msg = kmalloc(size, GFP_KERNEL); 102 if (!msg) 103 return -ENOMEM; 104 msg->hdr.size = size; 105 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; 106 msg->num_elems = 1; 107 msg->dma[0].stream_tag = stream_tag; 108 msg->dma[0].dma_buffer_size = buffer_size; 109 110 ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 111 kfree(msg); 112 return ret; 113} 114 115/** 116 * sof_probes_deinit - cleanup after data probing 117 * @cdev: SOF client device 118 * 119 * Host sends DEINIT request to free previously initialized probe 120 * on DSP side once it is no longer needed. DEINIT only when there 121 * are no probes connected and with all injectors detached. 122 */ 123static int sof_probes_deinit(struct sof_client_dev *cdev) 124{ 125 struct sof_ipc_cmd_hdr msg; 126 struct sof_ipc_reply reply; 127 128 msg.size = sizeof(msg); 129 msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; 130 131 return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply)); 132} 133 134static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd, 135 void **params, size_t *num_params) 136{ 137 size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev); 138 struct sof_ipc_probe_info_params msg = {{{0}}}; 139 struct sof_ipc_probe_info_params *reply; 140 size_t bytes; 141 int ret; 142 143 *params = NULL; 144 *num_params = 0; 145 146 reply = kzalloc(max_msg_size, GFP_KERNEL); 147 if (!reply) 148 return -ENOMEM; 149 msg.rhdr.hdr.size = sizeof(msg); 150 msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; 151 152 ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size); 153 if (ret < 0 || reply->rhdr.error < 0) 154 goto exit; 155 156 if (!reply->num_elems) 157 goto exit; 158 159 if (cmd == SOF_IPC_PROBE_DMA_INFO) 160 bytes = sizeof(reply->dma[0]); 161 else 162 bytes = sizeof(reply->desc[0]); 163 bytes *= reply->num_elems; 164 *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); 165 if (!*params) { 166 ret = -ENOMEM; 167 goto exit; 168 } 169 *num_params = reply->num_elems; 170 171exit: 172 kfree(reply); 173 return ret; 174} 175 176/** 177 * sof_probes_points_info - retrieve list of active probe points 178 * @cdev: SOF client device 179 * @desc: Returned list of active probes 180 * @num_desc: Returned count of active probes 181 * 182 * Host sends PROBE_POINT_INFO request to obtain list of active probe 183 * points, valid for disconnection when given probe is no longer 184 * required. 185 */ 186static int sof_probes_points_info(struct sof_client_dev *cdev, 187 struct sof_probe_point_desc **desc, 188 size_t *num_desc) 189{ 190 return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO, 191 (void **)desc, num_desc); 192} 193 194/** 195 * sof_probes_points_add - connect specified probes 196 * @cdev: SOF client device 197 * @desc: List of probe points to connect 198 * @num_desc: Number of elements in @desc 199 * 200 * Dynamically connects to provided set of endpoints. Immediately 201 * after connection is established, host must be prepared to 202 * transfer data from or to target stream given the probing purpose. 203 * 204 * Each probe point should be removed using PROBE_POINT_REMOVE 205 * request when no longer needed. 206 */ 207static int sof_probes_points_add(struct sof_client_dev *cdev, 208 struct sof_probe_point_desc *desc, 209 size_t num_desc) 210{ 211 struct sof_ipc_probe_point_add_params *msg; 212 size_t size = struct_size(msg, desc, num_desc); 213 struct sof_ipc_reply reply; 214 int ret; 215 216 msg = kmalloc(size, GFP_KERNEL); 217 if (!msg) 218 return -ENOMEM; 219 msg->hdr.size = size; 220 msg->num_elems = num_desc; 221 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; 222 memcpy(&msg->desc[0], desc, size - sizeof(*msg)); 223 224 ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 225 kfree(msg); 226 return ret; 227} 228 229/** 230 * sof_probes_points_remove - disconnect specified probes 231 * @cdev: SOF client device 232 * @buffer_id: List of probe points to disconnect 233 * @num_buffer_id: Number of elements in @desc 234 * 235 * Removes previously connected probes from list of active probe 236 * points and frees all resources on DSP side. 237 */ 238static int sof_probes_points_remove(struct sof_client_dev *cdev, 239 unsigned int *buffer_id, size_t num_buffer_id) 240{ 241 struct sof_ipc_probe_point_remove_params *msg; 242 size_t size = struct_size(msg, buffer_id, num_buffer_id); 243 struct sof_ipc_reply reply; 244 int ret; 245 246 msg = kmalloc(size, GFP_KERNEL); 247 if (!msg) 248 return -ENOMEM; 249 msg->hdr.size = size; 250 msg->num_elems = num_buffer_id; 251 msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; 252 memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); 253 254 ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply)); 255 kfree(msg); 256 return ret; 257} 258 259static int sof_probes_compr_startup(struct snd_compr_stream *cstream, 260 struct snd_soc_dai *dai) 261{ 262 struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); 263 struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); 264 struct sof_probes_priv *priv = cdev->data; 265 const struct sof_probes_host_ops *ops = priv->host_ops; 266 int ret; 267 268 if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) 269 return -ENODEV; 270 271 ret = sof_client_core_module_get(cdev); 272 if (ret) 273 return ret; 274 275 ret = ops->startup(cdev, cstream, dai, &priv->extractor_stream_tag); 276 if (ret) { 277 dev_err(dai->dev, "Failed to startup probe stream: %d\n", ret); 278 priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; 279 sof_client_core_module_put(cdev); 280 } 281 282 return ret; 283} 284 285static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream, 286 struct snd_soc_dai *dai) 287{ 288 struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); 289 struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); 290 struct sof_probes_priv *priv = cdev->data; 291 const struct sof_probes_host_ops *ops = priv->host_ops; 292 struct sof_probe_point_desc *desc; 293 size_t num_desc; 294 int i, ret; 295 296 /* disconnect all probe points */ 297 ret = sof_probes_points_info(cdev, &desc, &num_desc); 298 if (ret < 0) { 299 dev_err(dai->dev, "Failed to get probe points: %d\n", ret); 300 goto exit; 301 } 302 303 for (i = 0; i < num_desc; i++) 304 sof_probes_points_remove(cdev, &desc[i].buffer_id, 1); 305 kfree(desc); 306 307exit: 308 ret = sof_probes_deinit(cdev); 309 if (ret < 0) 310 dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); 311 312 priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; 313 snd_compr_free_pages(cstream); 314 315 ret = ops->shutdown(cdev, cstream, dai); 316 317 sof_client_core_module_put(cdev); 318 319 return ret; 320} 321 322static int sof_probes_compr_set_params(struct snd_compr_stream *cstream, 323 struct snd_compr_params *params, 324 struct snd_soc_dai *dai) 325{ 326 struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); 327 struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); 328 struct snd_compr_runtime *rtd = cstream->runtime; 329 struct sof_probes_priv *priv = cdev->data; 330 const struct sof_probes_host_ops *ops = priv->host_ops; 331 int ret; 332 333 cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; 334 cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev); 335 ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); 336 if (ret < 0) 337 return ret; 338 339 ret = ops->set_params(cdev, cstream, params, dai); 340 if (ret) 341 return ret; 342 343 ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes); 344 if (ret < 0) { 345 dev_err(dai->dev, "Failed to init probe: %d\n", ret); 346 return ret; 347 } 348 349 return 0; 350} 351 352static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd, 353 struct snd_soc_dai *dai) 354{ 355 struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); 356 struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); 357 struct sof_probes_priv *priv = cdev->data; 358 const struct sof_probes_host_ops *ops = priv->host_ops; 359 360 return ops->trigger(cdev, cstream, cmd, dai); 361} 362 363static int sof_probes_compr_pointer(struct snd_compr_stream *cstream, 364 struct snd_compr_tstamp *tstamp, 365 struct snd_soc_dai *dai) 366{ 367 struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component); 368 struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card); 369 struct sof_probes_priv *priv = cdev->data; 370 const struct sof_probes_host_ops *ops = priv->host_ops; 371 372 return ops->pointer(cdev, cstream, tstamp, dai); 373} 374 375static const struct snd_soc_cdai_ops sof_probes_compr_ops = { 376 .startup = sof_probes_compr_startup, 377 .shutdown = sof_probes_compr_shutdown, 378 .set_params = sof_probes_compr_set_params, 379 .trigger = sof_probes_compr_trigger, 380 .pointer = sof_probes_compr_pointer, 381}; 382 383static int sof_probes_compr_copy(struct snd_soc_component *component, 384 struct snd_compr_stream *cstream, 385 char __user *buf, size_t count) 386{ 387 struct snd_compr_runtime *rtd = cstream->runtime; 388 unsigned int offset, n; 389 void *ptr; 390 int ret; 391 392 if (count > rtd->buffer_size) 393 count = rtd->buffer_size; 394 395 div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); 396 ptr = rtd->dma_area + offset; 397 n = rtd->buffer_size - offset; 398 399 if (count < n) { 400 ret = copy_to_user(buf, ptr, count); 401 } else { 402 ret = copy_to_user(buf, ptr, n); 403 ret += copy_to_user(buf + n, rtd->dma_area, count - n); 404 } 405 406 if (ret) 407 return count - ret; 408 return count; 409} 410 411static const struct snd_compress_ops sof_probes_compressed_ops = { 412 .copy = sof_probes_compr_copy, 413}; 414 415static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, 416 size_t count, loff_t *ppos) 417{ 418 struct sof_client_dev *cdev = file->private_data; 419 struct sof_probes_priv *priv = cdev->data; 420 struct device *dev = &cdev->auxdev.dev; 421 struct sof_probe_point_desc *desc; 422 int remaining, offset; 423 size_t num_desc; 424 char *buf; 425 int i, ret, err; 426 427 if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { 428 dev_warn(dev, "no extractor stream running\n"); 429 return -ENOENT; 430 } 431 432 buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 433 if (!buf) 434 return -ENOMEM; 435 436 ret = pm_runtime_resume_and_get(dev); 437 if (ret < 0 && ret != -EACCES) { 438 dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret); 439 goto exit; 440 } 441 442 ret = sof_probes_points_info(cdev, &desc, &num_desc); 443 if (ret < 0) 444 goto exit; 445 446 pm_runtime_mark_last_busy(dev); 447 err = pm_runtime_put_autosuspend(dev); 448 if (err < 0) 449 dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); 450 451 for (i = 0; i < num_desc; i++) { 452 offset = strlen(buf); 453 remaining = PAGE_SIZE - offset; 454 ret = snprintf(buf + offset, remaining, 455 "Id: %#010x Purpose: %u Node id: %#x\n", 456 desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); 457 if (ret < 0 || ret >= remaining) { 458 /* truncate the output buffer at the last full line */ 459 buf[offset] = '\0'; 460 break; 461 } 462 } 463 464 ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf)); 465 466 kfree(desc); 467exit: 468 kfree(buf); 469 return ret; 470} 471 472static ssize_t 473sof_probes_dfs_points_write(struct file *file, const char __user *from, 474 size_t count, loff_t *ppos) 475{ 476 struct sof_client_dev *cdev = file->private_data; 477 struct sof_probes_priv *priv = cdev->data; 478 struct device *dev = &cdev->auxdev.dev; 479 struct sof_probe_point_desc *desc; 480 u32 num_elems, *array; 481 size_t bytes; 482 int ret, err; 483 484 if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { 485 dev_warn(dev, "no extractor stream running\n"); 486 return -ENOENT; 487 } 488 489 ret = parse_int_array_user(from, count, (int **)&array); 490 if (ret < 0) 491 return ret; 492 493 num_elems = *array; 494 bytes = sizeof(*array) * num_elems; 495 if (bytes % sizeof(*desc)) { 496 ret = -EINVAL; 497 goto exit; 498 } 499 500 desc = (struct sof_probe_point_desc *)&array[1]; 501 502 ret = pm_runtime_resume_and_get(dev); 503 if (ret < 0 && ret != -EACCES) { 504 dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); 505 goto exit; 506 } 507 508 ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc)); 509 if (!ret) 510 ret = count; 511 512 pm_runtime_mark_last_busy(dev); 513 err = pm_runtime_put_autosuspend(dev); 514 if (err < 0) 515 dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); 516exit: 517 kfree(array); 518 return ret; 519} 520 521static const struct file_operations sof_probes_points_fops = { 522 .open = simple_open, 523 .read = sof_probes_dfs_points_read, 524 .write = sof_probes_dfs_points_write, 525 .llseek = default_llseek, 526 527 .owner = THIS_MODULE, 528}; 529 530static ssize_t 531sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, 532 size_t count, loff_t *ppos) 533{ 534 struct sof_client_dev *cdev = file->private_data; 535 struct sof_probes_priv *priv = cdev->data; 536 struct device *dev = &cdev->auxdev.dev; 537 int ret, err; 538 u32 *array; 539 540 if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) { 541 dev_warn(dev, "no extractor stream running\n"); 542 return -ENOENT; 543 } 544 545 ret = parse_int_array_user(from, count, (int **)&array); 546 if (ret < 0) 547 return ret; 548 549 ret = pm_runtime_resume_and_get(dev); 550 if (ret < 0) { 551 dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); 552 goto exit; 553 } 554 555 ret = sof_probes_points_remove(cdev, &array[1], array[0]); 556 if (!ret) 557 ret = count; 558 559 pm_runtime_mark_last_busy(dev); 560 err = pm_runtime_put_autosuspend(dev); 561 if (err < 0) 562 dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); 563exit: 564 kfree(array); 565 return ret; 566} 567 568static const struct file_operations sof_probes_points_remove_fops = { 569 .open = simple_open, 570 .write = sof_probes_dfs_points_remove_write, 571 .llseek = default_llseek, 572 573 .owner = THIS_MODULE, 574}; 575 576static struct snd_soc_dai_driver sof_probes_dai_drv[] = { 577{ 578 .name = "Probe Extraction CPU DAI", 579 .compress_new = snd_soc_new_compress, 580 .cops = &sof_probes_compr_ops, 581 .capture = { 582 .stream_name = "Probe Extraction", 583 .channels_min = 1, 584 .channels_max = 8, 585 .rates = SNDRV_PCM_RATE_48000, 586 .rate_min = 48000, 587 .rate_max = 48000, 588 }, 589}, 590}; 591 592static const struct snd_soc_component_driver sof_probes_component = { 593 .name = "sof-probes-component", 594 .compress_ops = &sof_probes_compressed_ops, 595 .module_get_upon_open = 1, 596 .legacy_dai_naming = 1, 597}; 598 599SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); 600 601static int sof_probes_client_probe(struct auxiliary_device *auxdev, 602 const struct auxiliary_device_id *id) 603{ 604 struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); 605 struct dentry *dfsroot = sof_client_get_debugfs_root(cdev); 606 struct device *dev = &auxdev->dev; 607 struct snd_soc_dai_link_component platform_component[] = { 608 { 609 .name = dev_name(dev), 610 } 611 }; 612 struct snd_soc_card *card; 613 struct sof_probes_priv *priv; 614 struct snd_soc_dai_link_component *cpus; 615 struct sof_probes_host_ops *ops; 616 struct snd_soc_dai_link *links; 617 int ret; 618 619 /* do not set up the probes support if it is not enabled */ 620 if (!sof_probes_enabled) 621 return -ENXIO; 622 623 /* only ipc3 is supported */ 624 if (sof_client_get_ipc_type(cdev) != SOF_IPC) 625 return -ENXIO; 626 627 if (!dev->platform_data) { 628 dev_err(dev, "missing platform data\n"); 629 return -ENODEV; 630 } 631 632 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 633 if (!priv) 634 return -ENOMEM; 635 636 ops = dev->platform_data; 637 638 if (!ops->startup || !ops->shutdown || !ops->set_params || !ops->trigger || 639 !ops->pointer) { 640 dev_err(dev, "missing platform callback(s)\n"); 641 return -ENODEV; 642 } 643 644 priv->host_ops = ops; 645 cdev->data = priv; 646 647 /* register probes component driver and dai */ 648 ret = devm_snd_soc_register_component(dev, &sof_probes_component, 649 sof_probes_dai_drv, 650 ARRAY_SIZE(sof_probes_dai_drv)); 651 if (ret < 0) { 652 dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret); 653 return ret; 654 } 655 656 /* set client data */ 657 priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID; 658 659 /* create read-write probes_points debugfs entry */ 660 priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot, 661 cdev, &sof_probes_points_fops); 662 663 /* create read-write probe_points_remove debugfs entry */ 664 priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644, 665 dfsroot, cdev, 666 &sof_probes_points_remove_fops); 667 668 links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL); 669 cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL); 670 if (!links || !cpus) { 671 debugfs_remove(priv->dfs_points); 672 debugfs_remove(priv->dfs_points_remove); 673 return -ENOMEM; 674 } 675 676 /* extraction DAI link */ 677 links[0].name = "Compress Probe Capture"; 678 links[0].id = 0; 679 links[0].cpus = &cpus[0]; 680 links[0].num_cpus = 1; 681 links[0].cpus->dai_name = "Probe Extraction CPU DAI"; 682 links[0].codecs = dummy; 683 links[0].num_codecs = 1; 684 links[0].platforms = platform_component; 685 links[0].num_platforms = ARRAY_SIZE(platform_component); 686 links[0].nonatomic = 1; 687 688 card = &priv->card; 689 690 card->dev = dev; 691 card->name = "sof-probes"; 692 card->owner = THIS_MODULE; 693 card->num_links = SOF_PROBES_NUM_DAI_LINKS; 694 card->dai_link = links; 695 696 /* set idle_bias_off to prevent the core from resuming the card->dev */ 697 card->dapm.idle_bias_off = true; 698 699 snd_soc_card_set_drvdata(card, cdev); 700 701 ret = devm_snd_soc_register_card(dev, card); 702 if (ret < 0) { 703 debugfs_remove(priv->dfs_points); 704 debugfs_remove(priv->dfs_points_remove); 705 dev_err(dev, "Probes card register failed %d\n", ret); 706 return ret; 707 } 708 709 /* enable runtime PM */ 710 pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS); 711 pm_runtime_use_autosuspend(dev); 712 pm_runtime_enable(dev); 713 pm_runtime_mark_last_busy(dev); 714 pm_runtime_idle(dev); 715 716 return 0; 717} 718 719static void sof_probes_client_remove(struct auxiliary_device *auxdev) 720{ 721 struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); 722 struct sof_probes_priv *priv = cdev->data; 723 724 if (!sof_probes_enabled) 725 return; 726 727 pm_runtime_disable(&auxdev->dev); 728 debugfs_remove(priv->dfs_points); 729 debugfs_remove(priv->dfs_points_remove); 730} 731 732static const struct auxiliary_device_id sof_probes_client_id_table[] = { 733 { .name = "snd_sof.hda-probes", }, 734 {}, 735}; 736MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table); 737 738/* driver name will be set based on KBUILD_MODNAME */ 739static struct auxiliary_driver sof_probes_client_drv = { 740 .probe = sof_probes_client_probe, 741 .remove = sof_probes_client_remove, 742 743 .id_table = sof_probes_client_id_table, 744}; 745 746module_auxiliary_driver(sof_probes_client_drv); 747 748MODULE_DESCRIPTION("SOF Probes Client Driver"); 749MODULE_LICENSE("GPL v2"); 750MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);