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