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 28 static bool __read_mostly sof_probes_enabled; 29 module_param_named(enable, sof_probes_enabled, bool, 0444); 30 MODULE_PARM_DESC(enable, "Enable SOF probes support"); 31 32 struct 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 41 struct sof_probe_point_desc { 42 unsigned int buffer_id; 43 unsigned int purpose; 44 unsigned int stream_tag; 45 } __packed; 46 47 struct sof_probe_dma { 48 unsigned int stream_tag; 49 unsigned int dma_buffer_size; 50 } __packed; 51 52 struct 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 58 struct 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 67 struct 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 73 struct 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 */ 93 static 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 */ 123 static 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 134 static 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 171 exit: 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 */ 186 static 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 */ 207 static 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 */ 238 static 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 259 static 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 285 static 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 307 exit: 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 322 static 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 352 static 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 363 static 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 375 static 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 383 static 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 411 static const struct snd_compress_ops sof_probes_compressed_ops = { 412 .copy = sof_probes_compr_copy, 413 }; 414 415 static 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); 467 exit: 468 kfree(buf); 469 return ret; 470 } 471 472 static ssize_t 473 sof_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); 516 exit: 517 kfree(array); 518 return ret; 519 } 520 521 static 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 530 static ssize_t 531 sof_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); 563 exit: 564 kfree(array); 565 return ret; 566 } 567 568 static 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 576 static 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 592 static 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 599 SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); 600 601 static 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 719 static 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 732 static const struct auxiliary_device_id sof_probes_client_id_table[] = { 733 { .name = "snd_sof.hda-probes", }, 734 {}, 735 }; 736 MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table); 737 738 /* driver name will be set based on KBUILD_MODNAME */ 739 static 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 746 module_auxiliary_driver(sof_probes_client_drv); 747 748 MODULE_DESCRIPTION("SOF Probes Client Driver"); 749 MODULE_LICENSE("GPL v2"); 750 MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); 751