Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright(c) 2019-2022 Intel Corporation. All rights reserved.
0004 //
0005 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
0006 //
0007 // SOF client support:
0008 //  Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
0009 //  Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
0010 //
0011 
0012 #include <linux/debugfs.h>
0013 #include <linux/module.h>
0014 #include <linux/pm_runtime.h>
0015 #include <sound/soc.h>
0016 #include <sound/sof/header.h>
0017 #include "sof-client.h"
0018 #include "sof-client-probes.h"
0019 
0020 #define SOF_PROBES_SUSPEND_DELAY_MS 3000
0021 /* only extraction supported for now */
0022 #define SOF_PROBES_NUM_DAI_LINKS 1
0023 
0024 #define SOF_PROBES_INVALID_NODE_ID UINT_MAX
0025 
0026 static bool __read_mostly sof_probes_enabled;
0027 module_param_named(enable, sof_probes_enabled, bool, 0444);
0028 MODULE_PARM_DESC(enable, "Enable SOF probes support");
0029 
0030 struct sof_probes_priv {
0031     struct dentry *dfs_points;
0032     struct dentry *dfs_points_remove;
0033     u32 extractor_stream_tag;
0034     struct snd_soc_card card;
0035 
0036     const struct sof_probes_host_ops *host_ops;
0037 };
0038 
0039 struct sof_probe_point_desc {
0040     unsigned int buffer_id;
0041     unsigned int purpose;
0042     unsigned int stream_tag;
0043 } __packed;
0044 
0045 struct sof_probe_dma {
0046     unsigned int stream_tag;
0047     unsigned int dma_buffer_size;
0048 } __packed;
0049 
0050 struct sof_ipc_probe_dma_add_params {
0051     struct sof_ipc_cmd_hdr hdr;
0052     unsigned int num_elems;
0053     struct sof_probe_dma dma[];
0054 } __packed;
0055 
0056 struct sof_ipc_probe_info_params {
0057     struct sof_ipc_reply rhdr;
0058     unsigned int num_elems;
0059     union {
0060         struct sof_probe_dma dma[0];
0061         struct sof_probe_point_desc desc[0];
0062     };
0063 } __packed;
0064 
0065 struct sof_ipc_probe_point_add_params {
0066     struct sof_ipc_cmd_hdr hdr;
0067     unsigned int num_elems;
0068     struct sof_probe_point_desc desc[];
0069 } __packed;
0070 
0071 struct sof_ipc_probe_point_remove_params {
0072     struct sof_ipc_cmd_hdr hdr;
0073     unsigned int num_elems;
0074     unsigned int buffer_id[];
0075 } __packed;
0076 
0077 /**
0078  * sof_probes_init - initialize data probing
0079  * @cdev:       SOF client device
0080  * @stream_tag:     Extractor stream tag
0081  * @buffer_size:    DMA buffer size to set for extractor
0082  *
0083  * Host chooses whether extraction is supported or not by providing
0084  * valid stream tag to DSP. Once specified, stream described by that
0085  * tag will be tied to DSP for extraction for the entire lifetime of
0086  * probe.
0087  *
0088  * Probing is initialized only once and each INIT request must be
0089  * matched by DEINIT call.
0090  */
0091 static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
0092                size_t buffer_size)
0093 {
0094     struct sof_ipc_probe_dma_add_params *msg;
0095     size_t size = struct_size(msg, dma, 1);
0096     struct sof_ipc_reply reply;
0097     int ret;
0098 
0099     msg = kmalloc(size, GFP_KERNEL);
0100     if (!msg)
0101         return -ENOMEM;
0102     msg->hdr.size = size;
0103     msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT;
0104     msg->num_elems = 1;
0105     msg->dma[0].stream_tag = stream_tag;
0106     msg->dma[0].dma_buffer_size = buffer_size;
0107 
0108     ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
0109     kfree(msg);
0110     return ret;
0111 }
0112 
0113 /**
0114  * sof_probes_deinit - cleanup after data probing
0115  * @cdev:       SOF client device
0116  *
0117  * Host sends DEINIT request to free previously initialized probe
0118  * on DSP side once it is no longer needed. DEINIT only when there
0119  * are no probes connected and with all injectors detached.
0120  */
0121 static int sof_probes_deinit(struct sof_client_dev *cdev)
0122 {
0123     struct sof_ipc_cmd_hdr msg;
0124     struct sof_ipc_reply reply;
0125 
0126     msg.size = sizeof(msg);
0127     msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
0128 
0129     return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply));
0130 }
0131 
0132 static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
0133                void **params, size_t *num_params)
0134 {
0135     size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
0136     struct sof_ipc_probe_info_params msg = {{{0}}};
0137     struct sof_ipc_probe_info_params *reply;
0138     size_t bytes;
0139     int ret;
0140 
0141     *params = NULL;
0142     *num_params = 0;
0143 
0144     reply = kzalloc(max_msg_size, GFP_KERNEL);
0145     if (!reply)
0146         return -ENOMEM;
0147     msg.rhdr.hdr.size = sizeof(msg);
0148     msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
0149 
0150     ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size);
0151     if (ret < 0 || reply->rhdr.error < 0)
0152         goto exit;
0153 
0154     if (!reply->num_elems)
0155         goto exit;
0156 
0157     if (cmd == SOF_IPC_PROBE_DMA_INFO)
0158         bytes = sizeof(reply->dma[0]);
0159     else
0160         bytes = sizeof(reply->desc[0]);
0161     bytes *= reply->num_elems;
0162     *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
0163     if (!*params) {
0164         ret = -ENOMEM;
0165         goto exit;
0166     }
0167     *num_params = reply->num_elems;
0168 
0169 exit:
0170     kfree(reply);
0171     return ret;
0172 }
0173 
0174 /**
0175  * sof_probes_points_info - retrieve list of active probe points
0176  * @cdev:       SOF client device
0177  * @desc:   Returned list of active probes
0178  * @num_desc:   Returned count of active probes
0179  *
0180  * Host sends PROBE_POINT_INFO request to obtain list of active probe
0181  * points, valid for disconnection when given probe is no longer
0182  * required.
0183  */
0184 static int sof_probes_points_info(struct sof_client_dev *cdev,
0185                   struct sof_probe_point_desc **desc,
0186                   size_t *num_desc)
0187 {
0188     return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
0189                    (void **)desc, num_desc);
0190 }
0191 
0192 /**
0193  * sof_probes_points_add - connect specified probes
0194  * @cdev:       SOF client device
0195  * @desc:   List of probe points to connect
0196  * @num_desc:   Number of elements in @desc
0197  *
0198  * Dynamically connects to provided set of endpoints. Immediately
0199  * after connection is established, host must be prepared to
0200  * transfer data from or to target stream given the probing purpose.
0201  *
0202  * Each probe point should be removed using PROBE_POINT_REMOVE
0203  * request when no longer needed.
0204  */
0205 static int sof_probes_points_add(struct sof_client_dev *cdev,
0206                  struct sof_probe_point_desc *desc,
0207                  size_t num_desc)
0208 {
0209     struct sof_ipc_probe_point_add_params *msg;
0210     size_t size = struct_size(msg, desc, num_desc);
0211     struct sof_ipc_reply reply;
0212     int ret;
0213 
0214     msg = kmalloc(size, GFP_KERNEL);
0215     if (!msg)
0216         return -ENOMEM;
0217     msg->hdr.size = size;
0218     msg->num_elems = num_desc;
0219     msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
0220     memcpy(&msg->desc[0], desc, size - sizeof(*msg));
0221 
0222     ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
0223     kfree(msg);
0224     return ret;
0225 }
0226 
0227 /**
0228  * sof_probes_points_remove - disconnect specified probes
0229  * @cdev:       SOF client device
0230  * @buffer_id:      List of probe points to disconnect
0231  * @num_buffer_id:  Number of elements in @desc
0232  *
0233  * Removes previously connected probes from list of active probe
0234  * points and frees all resources on DSP side.
0235  */
0236 static int sof_probes_points_remove(struct sof_client_dev *cdev,
0237                     unsigned int *buffer_id, size_t num_buffer_id)
0238 {
0239     struct sof_ipc_probe_point_remove_params *msg;
0240     size_t size = struct_size(msg, buffer_id, num_buffer_id);
0241     struct sof_ipc_reply reply;
0242     int ret;
0243 
0244     msg = kmalloc(size, GFP_KERNEL);
0245     if (!msg)
0246         return -ENOMEM;
0247     msg->hdr.size = size;
0248     msg->num_elems = num_buffer_id;
0249     msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
0250     memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
0251 
0252     ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
0253     kfree(msg);
0254     return ret;
0255 }
0256 
0257 static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
0258                     struct snd_soc_dai *dai)
0259 {
0260     struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
0261     struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
0262     struct sof_probes_priv *priv = cdev->data;
0263     const struct sof_probes_host_ops *ops = priv->host_ops;
0264     int ret;
0265 
0266     if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
0267         return -ENODEV;
0268 
0269     ret = sof_client_core_module_get(cdev);
0270     if (ret)
0271         return ret;
0272 
0273     ret = ops->startup(cdev, cstream, dai, &priv->extractor_stream_tag);
0274     if (ret) {
0275         dev_err(dai->dev, "Failed to startup probe stream: %d\n", ret);
0276         priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
0277         sof_client_core_module_put(cdev);
0278     }
0279 
0280     return ret;
0281 }
0282 
0283 static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
0284                      struct snd_soc_dai *dai)
0285 {
0286     struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
0287     struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
0288     struct sof_probes_priv *priv = cdev->data;
0289     const struct sof_probes_host_ops *ops = priv->host_ops;
0290     struct sof_probe_point_desc *desc;
0291     size_t num_desc;
0292     int i, ret;
0293 
0294     /* disconnect all probe points */
0295     ret = sof_probes_points_info(cdev, &desc, &num_desc);
0296     if (ret < 0) {
0297         dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
0298         goto exit;
0299     }
0300 
0301     for (i = 0; i < num_desc; i++)
0302         sof_probes_points_remove(cdev, &desc[i].buffer_id, 1);
0303     kfree(desc);
0304 
0305 exit:
0306     ret = sof_probes_deinit(cdev);
0307     if (ret < 0)
0308         dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
0309 
0310     priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
0311     snd_compr_free_pages(cstream);
0312 
0313     ret = ops->shutdown(cdev, cstream, dai);
0314 
0315     sof_client_core_module_put(cdev);
0316 
0317     return ret;
0318 }
0319 
0320 static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
0321                        struct snd_compr_params *params,
0322                        struct snd_soc_dai *dai)
0323 {
0324     struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
0325     struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
0326     struct snd_compr_runtime *rtd = cstream->runtime;
0327     struct sof_probes_priv *priv = cdev->data;
0328     const struct sof_probes_host_ops *ops = priv->host_ops;
0329     int ret;
0330 
0331     cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
0332     cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
0333     ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
0334     if (ret < 0)
0335         return ret;
0336 
0337     ret = ops->set_params(cdev, cstream, params, dai);
0338     if (ret)
0339         return ret;
0340 
0341     ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
0342     if (ret < 0) {
0343         dev_err(dai->dev, "Failed to init probe: %d\n", ret);
0344         return ret;
0345     }
0346 
0347     return 0;
0348 }
0349 
0350 static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
0351                     struct snd_soc_dai *dai)
0352 {
0353     struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
0354     struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
0355     struct sof_probes_priv *priv = cdev->data;
0356     const struct sof_probes_host_ops *ops = priv->host_ops;
0357 
0358     return ops->trigger(cdev, cstream, cmd, dai);
0359 }
0360 
0361 static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
0362                     struct snd_compr_tstamp *tstamp,
0363                     struct snd_soc_dai *dai)
0364 {
0365     struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
0366     struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
0367     struct sof_probes_priv *priv = cdev->data;
0368     const struct sof_probes_host_ops *ops = priv->host_ops;
0369 
0370     return ops->pointer(cdev, cstream, tstamp, dai);
0371 }
0372 
0373 static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
0374     .startup = sof_probes_compr_startup,
0375     .shutdown = sof_probes_compr_shutdown,
0376     .set_params = sof_probes_compr_set_params,
0377     .trigger = sof_probes_compr_trigger,
0378     .pointer = sof_probes_compr_pointer,
0379 };
0380 
0381 static int sof_probes_compr_copy(struct snd_soc_component *component,
0382                  struct snd_compr_stream *cstream,
0383                  char __user *buf, size_t count)
0384 {
0385     struct snd_compr_runtime *rtd = cstream->runtime;
0386     unsigned int offset, n;
0387     void *ptr;
0388     int ret;
0389 
0390     if (count > rtd->buffer_size)
0391         count = rtd->buffer_size;
0392 
0393     div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
0394     ptr = rtd->dma_area + offset;
0395     n = rtd->buffer_size - offset;
0396 
0397     if (count < n) {
0398         ret = copy_to_user(buf, ptr, count);
0399     } else {
0400         ret = copy_to_user(buf, ptr, n);
0401         ret += copy_to_user(buf + n, rtd->dma_area, count - n);
0402     }
0403 
0404     if (ret)
0405         return count - ret;
0406     return count;
0407 }
0408 
0409 static const struct snd_compress_ops sof_probes_compressed_ops = {
0410     .copy = sof_probes_compr_copy,
0411 };
0412 
0413 /**
0414  * strsplit_u32 - Split string into sequence of u32 tokens
0415  * @buf:    String to split into tokens.
0416  * @delim:  String containing delimiter characters.
0417  * @tkns:   Returned u32 sequence pointer.
0418  * @num_tkns:   Returned number of tokens obtained.
0419  */
0420 static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns)
0421 {
0422     char *s;
0423     u32 *data, *tmp;
0424     size_t count = 0;
0425     size_t cap = 32;
0426     int ret = 0;
0427 
0428     *tkns = NULL;
0429     *num_tkns = 0;
0430     data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
0431     if (!data)
0432         return -ENOMEM;
0433 
0434     while ((s = strsep(&buf, delim)) != NULL) {
0435         ret = kstrtouint(s, 0, data + count);
0436         if (ret)
0437             goto exit;
0438         if (++count >= cap) {
0439             cap *= 2;
0440             tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
0441             if (!tmp) {
0442                 ret = -ENOMEM;
0443                 goto exit;
0444             }
0445             data = tmp;
0446         }
0447     }
0448 
0449     if (!count)
0450         goto exit;
0451     *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
0452     if (!(*tkns)) {
0453         ret = -ENOMEM;
0454         goto exit;
0455     }
0456     *num_tkns = count;
0457 
0458 exit:
0459     kfree(data);
0460     return ret;
0461 }
0462 
0463 static int tokenize_input(const char __user *from, size_t count,
0464               loff_t *ppos, u32 **tkns, size_t *num_tkns)
0465 {
0466     char *buf;
0467     int ret;
0468 
0469     buf = kmalloc(count + 1, GFP_KERNEL);
0470     if (!buf)
0471         return -ENOMEM;
0472 
0473     ret = simple_write_to_buffer(buf, count, ppos, from, count);
0474     if (ret != count) {
0475         ret = ret >= 0 ? -EIO : ret;
0476         goto exit;
0477     }
0478 
0479     buf[count] = '\0';
0480     ret = strsplit_u32(buf, ",", tkns, num_tkns);
0481 exit:
0482     kfree(buf);
0483     return ret;
0484 }
0485 
0486 static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
0487                       size_t count, loff_t *ppos)
0488 {
0489     struct sof_client_dev *cdev = file->private_data;
0490     struct sof_probes_priv *priv = cdev->data;
0491     struct device *dev = &cdev->auxdev.dev;
0492     struct sof_probe_point_desc *desc;
0493     int remaining, offset;
0494     size_t num_desc;
0495     char *buf;
0496     int i, ret, err;
0497 
0498     if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
0499         dev_warn(dev, "no extractor stream running\n");
0500         return -ENOENT;
0501     }
0502 
0503     buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
0504     if (!buf)
0505         return -ENOMEM;
0506 
0507     ret = pm_runtime_resume_and_get(dev);
0508     if (ret < 0 && ret != -EACCES) {
0509         dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
0510         goto exit;
0511     }
0512 
0513     ret = sof_probes_points_info(cdev, &desc, &num_desc);
0514     if (ret < 0)
0515         goto exit;
0516 
0517     pm_runtime_mark_last_busy(dev);
0518     err = pm_runtime_put_autosuspend(dev);
0519     if (err < 0)
0520         dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
0521 
0522     for (i = 0; i < num_desc; i++) {
0523         offset = strlen(buf);
0524         remaining = PAGE_SIZE - offset;
0525         ret = snprintf(buf + offset, remaining,
0526                    "Id: %#010x  Purpose: %u  Node id: %#x\n",
0527                 desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
0528         if (ret < 0 || ret >= remaining) {
0529             /* truncate the output buffer at the last full line */
0530             buf[offset] = '\0';
0531             break;
0532         }
0533     }
0534 
0535     ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
0536 
0537     kfree(desc);
0538 exit:
0539     kfree(buf);
0540     return ret;
0541 }
0542 
0543 static ssize_t
0544 sof_probes_dfs_points_write(struct file *file, const char __user *from,
0545                 size_t count, loff_t *ppos)
0546 {
0547     struct sof_client_dev *cdev = file->private_data;
0548     struct sof_probes_priv *priv = cdev->data;
0549     struct device *dev = &cdev->auxdev.dev;
0550     struct sof_probe_point_desc *desc;
0551     size_t num_tkns, bytes;
0552     u32 *tkns;
0553     int ret, err;
0554 
0555     if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
0556         dev_warn(dev, "no extractor stream running\n");
0557         return -ENOENT;
0558     }
0559 
0560     ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
0561     if (ret < 0)
0562         return ret;
0563     bytes = sizeof(*tkns) * num_tkns;
0564     if (!num_tkns || (bytes % sizeof(*desc))) {
0565         ret = -EINVAL;
0566         goto exit;
0567     }
0568 
0569     desc = (struct sof_probe_point_desc *)tkns;
0570 
0571     ret = pm_runtime_resume_and_get(dev);
0572     if (ret < 0 && ret != -EACCES) {
0573         dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
0574         goto exit;
0575     }
0576 
0577     ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc));
0578     if (!ret)
0579         ret = count;
0580 
0581     pm_runtime_mark_last_busy(dev);
0582     err = pm_runtime_put_autosuspend(dev);
0583     if (err < 0)
0584         dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
0585 exit:
0586     kfree(tkns);
0587     return ret;
0588 }
0589 
0590 static const struct file_operations sof_probes_points_fops = {
0591     .open = simple_open,
0592     .read = sof_probes_dfs_points_read,
0593     .write = sof_probes_dfs_points_write,
0594     .llseek = default_llseek,
0595 
0596     .owner = THIS_MODULE,
0597 };
0598 
0599 static ssize_t
0600 sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
0601                    size_t count, loff_t *ppos)
0602 {
0603     struct sof_client_dev *cdev = file->private_data;
0604     struct sof_probes_priv *priv = cdev->data;
0605     struct device *dev = &cdev->auxdev.dev;
0606     size_t num_tkns;
0607     u32 *tkns;
0608     int ret, err;
0609 
0610     if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
0611         dev_warn(dev, "no extractor stream running\n");
0612         return -ENOENT;
0613     }
0614 
0615     ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
0616     if (ret < 0)
0617         return ret;
0618     if (!num_tkns) {
0619         ret = -EINVAL;
0620         goto exit;
0621     }
0622 
0623     ret = pm_runtime_resume_and_get(dev);
0624     if (ret < 0) {
0625         dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
0626         goto exit;
0627     }
0628 
0629     ret = sof_probes_points_remove(cdev, tkns, num_tkns);
0630     if (!ret)
0631         ret = count;
0632 
0633     pm_runtime_mark_last_busy(dev);
0634     err = pm_runtime_put_autosuspend(dev);
0635     if (err < 0)
0636         dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
0637 exit:
0638     kfree(tkns);
0639     return ret;
0640 }
0641 
0642 static const struct file_operations sof_probes_points_remove_fops = {
0643     .open = simple_open,
0644     .write = sof_probes_dfs_points_remove_write,
0645     .llseek = default_llseek,
0646 
0647     .owner = THIS_MODULE,
0648 };
0649 
0650 static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
0651 {
0652     .name = "Probe Extraction CPU DAI",
0653     .compress_new = snd_soc_new_compress,
0654     .cops = &sof_probes_compr_ops,
0655     .capture = {
0656         .stream_name = "Probe Extraction",
0657         .channels_min = 1,
0658         .channels_max = 8,
0659         .rates = SNDRV_PCM_RATE_48000,
0660         .rate_min = 48000,
0661         .rate_max = 48000,
0662     },
0663 },
0664 };
0665 
0666 static const struct snd_soc_component_driver sof_probes_component = {
0667     .name = "sof-probes-component",
0668     .compress_ops = &sof_probes_compressed_ops,
0669     .module_get_upon_open = 1,
0670     .legacy_dai_naming = 1,
0671 };
0672 
0673 SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
0674 
0675 static int sof_probes_client_probe(struct auxiliary_device *auxdev,
0676                    const struct auxiliary_device_id *id)
0677 {
0678     struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
0679     struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
0680     struct device *dev = &auxdev->dev;
0681     struct snd_soc_dai_link_component platform_component[] = {
0682         {
0683             .name = dev_name(dev),
0684         }
0685     };
0686     struct snd_soc_card *card;
0687     struct sof_probes_priv *priv;
0688     struct snd_soc_dai_link_component *cpus;
0689     struct sof_probes_host_ops *ops;
0690     struct snd_soc_dai_link *links;
0691     int ret;
0692 
0693     /* do not set up the probes support if it is not enabled */
0694     if (!sof_probes_enabled)
0695         return -ENXIO;
0696 
0697     /* only ipc3 is supported */
0698     if (sof_client_get_ipc_type(cdev) != SOF_IPC)
0699         return -ENXIO;
0700 
0701     if (!dev->platform_data) {
0702         dev_err(dev, "missing platform data\n");
0703         return -ENODEV;
0704     }
0705 
0706     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0707     if (!priv)
0708         return -ENOMEM;
0709 
0710     ops = dev->platform_data;
0711 
0712     if (!ops->startup || !ops->shutdown || !ops->set_params || !ops->trigger ||
0713         !ops->pointer) {
0714         dev_err(dev, "missing platform callback(s)\n");
0715         return -ENODEV;
0716     }
0717 
0718     priv->host_ops = ops;
0719     cdev->data = priv;
0720 
0721     /* register probes component driver and dai */
0722     ret = devm_snd_soc_register_component(dev, &sof_probes_component,
0723                           sof_probes_dai_drv,
0724                           ARRAY_SIZE(sof_probes_dai_drv));
0725     if (ret < 0) {
0726         dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
0727         return ret;
0728     }
0729 
0730     /* set client data */
0731     priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
0732 
0733     /* create read-write probes_points debugfs entry */
0734     priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
0735                            cdev, &sof_probes_points_fops);
0736 
0737     /* create read-write probe_points_remove debugfs entry */
0738     priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
0739                               dfsroot, cdev,
0740                               &sof_probes_points_remove_fops);
0741 
0742     links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
0743     cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
0744     if (!links || !cpus) {
0745         debugfs_remove(priv->dfs_points);
0746         debugfs_remove(priv->dfs_points_remove);
0747         return -ENOMEM;
0748     }
0749 
0750     /* extraction DAI link */
0751     links[0].name = "Compress Probe Capture";
0752     links[0].id = 0;
0753     links[0].cpus = &cpus[0];
0754     links[0].num_cpus = 1;
0755     links[0].cpus->dai_name = "Probe Extraction CPU DAI";
0756     links[0].codecs = dummy;
0757     links[0].num_codecs = 1;
0758     links[0].platforms = platform_component;
0759     links[0].num_platforms = ARRAY_SIZE(platform_component);
0760     links[0].nonatomic = 1;
0761 
0762     card = &priv->card;
0763 
0764     card->dev = dev;
0765     card->name = "sof-probes";
0766     card->owner = THIS_MODULE;
0767     card->num_links = SOF_PROBES_NUM_DAI_LINKS;
0768     card->dai_link = links;
0769 
0770     /* set idle_bias_off to prevent the core from resuming the card->dev */
0771     card->dapm.idle_bias_off = true;
0772 
0773     snd_soc_card_set_drvdata(card, cdev);
0774 
0775     ret = devm_snd_soc_register_card(dev, card);
0776     if (ret < 0) {
0777         debugfs_remove(priv->dfs_points);
0778         debugfs_remove(priv->dfs_points_remove);
0779         dev_err(dev, "Probes card register failed %d\n", ret);
0780         return ret;
0781     }
0782 
0783     /* enable runtime PM */
0784     pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
0785     pm_runtime_use_autosuspend(dev);
0786     pm_runtime_enable(dev);
0787     pm_runtime_mark_last_busy(dev);
0788     pm_runtime_idle(dev);
0789 
0790     return 0;
0791 }
0792 
0793 static void sof_probes_client_remove(struct auxiliary_device *auxdev)
0794 {
0795     struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
0796     struct sof_probes_priv *priv = cdev->data;
0797 
0798     if (!sof_probes_enabled)
0799         return;
0800 
0801     pm_runtime_disable(&auxdev->dev);
0802     debugfs_remove(priv->dfs_points);
0803     debugfs_remove(priv->dfs_points_remove);
0804 }
0805 
0806 static const struct auxiliary_device_id sof_probes_client_id_table[] = {
0807     { .name = "snd_sof.hda-probes", },
0808     {},
0809 };
0810 MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
0811 
0812 /* driver name will be set based on KBUILD_MODNAME */
0813 static struct auxiliary_driver sof_probes_client_drv = {
0814     .probe = sof_probes_client_probe,
0815     .remove = sof_probes_client_remove,
0816 
0817     .id_table = sof_probes_client_id_table,
0818 };
0819 
0820 module_auxiliary_driver(sof_probes_client_drv);
0821 
0822 MODULE_DESCRIPTION("SOF Probes Client Driver");
0823 MODULE_LICENSE("GPL v2");
0824 MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);