sof-client-probes.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. //
  3. // Copyright(c) 2019-2022 Intel Corporation
  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. #include <linux/debugfs.h>
  12. #include <linux/module.h>
  13. #include <linux/pm_runtime.h>
  14. #include <linux/string_helpers.h>
  15. #include <linux/stddef.h>
  16. #include <sound/soc.h>
  17. #include <sound/sof/header.h>
  18. #include "sof-client.h"
  19. #include "sof-client-probes.h"
  20. #define SOF_PROBES_SUSPEND_DELAY_MS 3000
  21. /* only extraction supported for now */
  22. #define SOF_PROBES_NUM_DAI_LINKS 1
  23. #define SOF_PROBES_INVALID_NODE_ID UINT_MAX
  24. static bool __read_mostly sof_probes_enabled;
  25. module_param_named(enable, sof_probes_enabled, bool, 0444);
  26. MODULE_PARM_DESC(enable, "Enable SOF probes support");
  27. static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
  28. struct snd_soc_dai *dai)
  29. {
  30. struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
  31. struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
  32. struct sof_probes_priv *priv = cdev->data;
  33. const struct sof_probes_host_ops *ops = priv->host_ops;
  34. int ret;
  35. if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
  36. return -ENODEV;
  37. ret = sof_client_core_module_get(cdev);
  38. if (ret)
  39. return ret;
  40. ret = ops->startup(cdev, cstream, dai, &priv->extractor_stream_tag);
  41. if (ret) {
  42. dev_err(dai->dev, "Failed to startup probe stream: %d\n", ret);
  43. priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
  44. sof_client_core_module_put(cdev);
  45. }
  46. return ret;
  47. }
  48. static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
  49. struct snd_soc_dai *dai)
  50. {
  51. struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
  52. struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
  53. struct sof_probes_priv *priv = cdev->data;
  54. const struct sof_probes_host_ops *ops = priv->host_ops;
  55. const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
  56. struct sof_probe_point_desc *desc;
  57. size_t num_desc;
  58. int i, ret;
  59. /* disconnect all probe points */
  60. ret = ipc->points_info(cdev, &desc, &num_desc);
  61. if (ret < 0) {
  62. dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
  63. goto exit;
  64. }
  65. for (i = 0; i < num_desc; i++)
  66. ipc->points_remove(cdev, &desc[i].buffer_id, 1);
  67. kfree(desc);
  68. exit:
  69. ret = ipc->deinit(cdev);
  70. if (ret < 0)
  71. dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
  72. priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
  73. snd_compr_free_pages(cstream);
  74. ret = ops->shutdown(cdev, cstream, dai);
  75. sof_client_core_module_put(cdev);
  76. return ret;
  77. }
  78. static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
  79. struct snd_compr_params *params,
  80. struct snd_soc_dai *dai)
  81. {
  82. struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
  83. struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
  84. struct snd_compr_runtime *rtd = cstream->runtime;
  85. struct sof_probes_priv *priv = cdev->data;
  86. const struct sof_probes_host_ops *ops = priv->host_ops;
  87. const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
  88. int ret;
  89. cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
  90. cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
  91. ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
  92. if (ret < 0)
  93. return ret;
  94. ret = ops->set_params(cdev, cstream, params, dai);
  95. if (ret)
  96. return ret;
  97. ret = ipc->init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
  98. if (ret < 0) {
  99. dev_err(dai->dev, "Failed to init probe: %d\n", ret);
  100. return ret;
  101. }
  102. return 0;
  103. }
  104. static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
  105. struct snd_soc_dai *dai)
  106. {
  107. struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
  108. struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
  109. struct sof_probes_priv *priv = cdev->data;
  110. const struct sof_probes_host_ops *ops = priv->host_ops;
  111. return ops->trigger(cdev, cstream, cmd, dai);
  112. }
  113. static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
  114. struct snd_compr_tstamp *tstamp,
  115. struct snd_soc_dai *dai)
  116. {
  117. struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
  118. struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
  119. struct sof_probes_priv *priv = cdev->data;
  120. const struct sof_probes_host_ops *ops = priv->host_ops;
  121. return ops->pointer(cdev, cstream, tstamp, dai);
  122. }
  123. static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
  124. .startup = sof_probes_compr_startup,
  125. .shutdown = sof_probes_compr_shutdown,
  126. .set_params = sof_probes_compr_set_params,
  127. .trigger = sof_probes_compr_trigger,
  128. .pointer = sof_probes_compr_pointer,
  129. };
  130. static int sof_probes_compr_copy(struct snd_soc_component *component,
  131. struct snd_compr_stream *cstream,
  132. char __user *buf, size_t count)
  133. {
  134. struct snd_compr_runtime *rtd = cstream->runtime;
  135. unsigned int offset, n;
  136. void *ptr;
  137. int ret;
  138. if (count > rtd->buffer_size)
  139. count = rtd->buffer_size;
  140. div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
  141. ptr = rtd->dma_area + offset;
  142. n = rtd->buffer_size - offset;
  143. if (count < n) {
  144. ret = copy_to_user(buf, ptr, count);
  145. } else {
  146. ret = copy_to_user(buf, ptr, n);
  147. ret += copy_to_user(buf + n, rtd->dma_area, count - n);
  148. }
  149. if (ret)
  150. return count - ret;
  151. return count;
  152. }
  153. static const struct snd_compress_ops sof_probes_compressed_ops = {
  154. .copy = sof_probes_compr_copy,
  155. };
  156. static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
  157. size_t count, loff_t *ppos)
  158. {
  159. struct sof_client_dev *cdev = file->private_data;
  160. struct sof_probes_priv *priv = cdev->data;
  161. struct device *dev = &cdev->auxdev.dev;
  162. struct sof_probe_point_desc *desc;
  163. const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
  164. int remaining, offset;
  165. size_t num_desc;
  166. char *buf;
  167. int i, ret, err;
  168. if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
  169. dev_warn(dev, "no extractor stream running\n");
  170. return -ENOENT;
  171. }
  172. buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
  173. if (!buf)
  174. return -ENOMEM;
  175. ret = pm_runtime_resume_and_get(dev);
  176. if (ret < 0 && ret != -EACCES) {
  177. dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
  178. goto exit;
  179. }
  180. ret = ipc->points_info(cdev, &desc, &num_desc);
  181. if (ret < 0)
  182. goto pm_error;
  183. for (i = 0; i < num_desc; i++) {
  184. offset = strlen(buf);
  185. remaining = PAGE_SIZE - offset;
  186. ret = snprintf(buf + offset, remaining,
  187. "Id: %#010x Purpose: %u Node id: %#x\n",
  188. desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
  189. if (ret < 0 || ret >= remaining) {
  190. /* truncate the output buffer at the last full line */
  191. buf[offset] = '\0';
  192. break;
  193. }
  194. }
  195. ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
  196. kfree(desc);
  197. pm_error:
  198. pm_runtime_mark_last_busy(dev);
  199. err = pm_runtime_put_autosuspend(dev);
  200. if (err < 0)
  201. dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
  202. exit:
  203. kfree(buf);
  204. return ret;
  205. }
  206. static ssize_t
  207. sof_probes_dfs_points_write(struct file *file, const char __user *from,
  208. size_t count, loff_t *ppos)
  209. {
  210. struct sof_client_dev *cdev = file->private_data;
  211. struct sof_probes_priv *priv = cdev->data;
  212. const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
  213. struct device *dev = &cdev->auxdev.dev;
  214. struct sof_probe_point_desc *desc;
  215. u32 num_elems, *array;
  216. size_t bytes;
  217. int ret, err;
  218. if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
  219. dev_warn(dev, "no extractor stream running\n");
  220. return -ENOENT;
  221. }
  222. ret = parse_int_array_user(from, count, (int **)&array);
  223. if (ret < 0)
  224. return ret;
  225. num_elems = *array;
  226. bytes = sizeof(*array) * num_elems;
  227. if (bytes % sizeof(*desc)) {
  228. ret = -EINVAL;
  229. goto exit;
  230. }
  231. desc = (struct sof_probe_point_desc *)&array[1];
  232. ret = pm_runtime_resume_and_get(dev);
  233. if (ret < 0 && ret != -EACCES) {
  234. dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
  235. goto exit;
  236. }
  237. ret = ipc->points_add(cdev, desc, bytes / sizeof(*desc));
  238. if (!ret)
  239. ret = count;
  240. pm_runtime_mark_last_busy(dev);
  241. err = pm_runtime_put_autosuspend(dev);
  242. if (err < 0)
  243. dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
  244. exit:
  245. kfree(array);
  246. return ret;
  247. }
  248. static const struct file_operations sof_probes_points_fops = {
  249. .open = simple_open,
  250. .read = sof_probes_dfs_points_read,
  251. .write = sof_probes_dfs_points_write,
  252. .llseek = default_llseek,
  253. .owner = THIS_MODULE,
  254. };
  255. static ssize_t
  256. sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
  257. size_t count, loff_t *ppos)
  258. {
  259. struct sof_client_dev *cdev = file->private_data;
  260. struct sof_probes_priv *priv = cdev->data;
  261. const struct sof_probes_ipc_ops *ipc = priv->ipc_ops;
  262. struct device *dev = &cdev->auxdev.dev;
  263. int ret, err;
  264. u32 *array;
  265. if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
  266. dev_warn(dev, "no extractor stream running\n");
  267. return -ENOENT;
  268. }
  269. ret = parse_int_array_user(from, count, (int **)&array);
  270. if (ret < 0)
  271. return ret;
  272. ret = pm_runtime_resume_and_get(dev);
  273. if (ret < 0) {
  274. dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
  275. goto exit;
  276. }
  277. ret = ipc->points_remove(cdev, &array[1], array[0]);
  278. if (!ret)
  279. ret = count;
  280. pm_runtime_mark_last_busy(dev);
  281. err = pm_runtime_put_autosuspend(dev);
  282. if (err < 0)
  283. dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
  284. exit:
  285. kfree(array);
  286. return ret;
  287. }
  288. static const struct file_operations sof_probes_points_remove_fops = {
  289. .open = simple_open,
  290. .write = sof_probes_dfs_points_remove_write,
  291. .llseek = default_llseek,
  292. .owner = THIS_MODULE,
  293. };
  294. static const struct snd_soc_dai_ops sof_probes_dai_ops = {
  295. .compress_new = snd_soc_new_compress,
  296. };
  297. static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
  298. {
  299. .name = "Probe Extraction CPU DAI",
  300. .ops = &sof_probes_dai_ops,
  301. .cops = &sof_probes_compr_ops,
  302. .capture = {
  303. .stream_name = "Probe Extraction",
  304. .channels_min = 1,
  305. .channels_max = 8,
  306. .rates = SNDRV_PCM_RATE_48000,
  307. .rate_min = 48000,
  308. .rate_max = 48000,
  309. },
  310. },
  311. };
  312. static const struct snd_soc_component_driver sof_probes_component = {
  313. .name = "sof-probes-component",
  314. .compress_ops = &sof_probes_compressed_ops,
  315. .module_get_upon_open = 1,
  316. .legacy_dai_naming = 1,
  317. };
  318. static int sof_probes_client_probe(struct auxiliary_device *auxdev,
  319. const struct auxiliary_device_id *id)
  320. {
  321. struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
  322. struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
  323. struct device *dev = &auxdev->dev;
  324. struct snd_soc_dai_link_component platform_component[] = {
  325. {
  326. .name = dev_name(dev),
  327. }
  328. };
  329. struct snd_soc_card *card;
  330. struct sof_probes_priv *priv;
  331. struct snd_soc_dai_link_component *cpus;
  332. struct sof_probes_host_ops *ops;
  333. struct snd_soc_dai_link *links;
  334. int ret;
  335. /* do not set up the probes support if it is not enabled */
  336. if (!sof_probes_enabled)
  337. return -ENXIO;
  338. ops = dev_get_platdata(dev);
  339. if (!ops) {
  340. dev_err(dev, "missing platform data\n");
  341. return -ENODEV;
  342. }
  343. if (!ops->startup || !ops->shutdown || !ops->set_params || !ops->trigger ||
  344. !ops->pointer) {
  345. dev_err(dev, "missing platform callback(s)\n");
  346. return -ENODEV;
  347. }
  348. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  349. if (!priv)
  350. return -ENOMEM;
  351. priv->host_ops = ops;
  352. switch (sof_client_get_ipc_type(cdev)) {
  353. #ifdef CONFIG_SND_SOC_SOF_IPC4
  354. case SOF_IPC_TYPE_4:
  355. priv->ipc_ops = &ipc4_probe_ops;
  356. break;
  357. #endif
  358. #ifdef CONFIG_SND_SOC_SOF_IPC3
  359. case SOF_IPC_TYPE_3:
  360. priv->ipc_ops = &ipc3_probe_ops;
  361. break;
  362. #endif
  363. default:
  364. dev_err(dev, "Matching IPC ops not found.");
  365. return -ENODEV;
  366. }
  367. cdev->data = priv;
  368. /* register probes component driver and dai */
  369. ret = devm_snd_soc_register_component(dev, &sof_probes_component,
  370. sof_probes_dai_drv,
  371. ARRAY_SIZE(sof_probes_dai_drv));
  372. if (ret < 0) {
  373. dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
  374. return ret;
  375. }
  376. /* set client data */
  377. priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
  378. /* create read-write probes_points debugfs entry */
  379. priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
  380. cdev, &sof_probes_points_fops);
  381. /* create read-write probe_points_remove debugfs entry */
  382. priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
  383. dfsroot, cdev,
  384. &sof_probes_points_remove_fops);
  385. links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
  386. cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
  387. if (!links || !cpus) {
  388. debugfs_remove(priv->dfs_points);
  389. debugfs_remove(priv->dfs_points_remove);
  390. return -ENOMEM;
  391. }
  392. /* extraction DAI link */
  393. links[0].name = "Compress Probe Capture";
  394. links[0].id = 0;
  395. links[0].cpus = &cpus[0];
  396. links[0].num_cpus = 1;
  397. links[0].cpus->dai_name = "Probe Extraction CPU DAI";
  398. links[0].codecs = &snd_soc_dummy_dlc;
  399. links[0].num_codecs = 1;
  400. links[0].platforms = platform_component;
  401. links[0].num_platforms = ARRAY_SIZE(platform_component);
  402. links[0].nonatomic = 1;
  403. card = &priv->card;
  404. card->dev = dev;
  405. card->name = "sof-probes";
  406. card->owner = THIS_MODULE;
  407. card->num_links = SOF_PROBES_NUM_DAI_LINKS;
  408. card->dai_link = links;
  409. /* set idle_bias_off to prevent the core from resuming the card->dev */
  410. card->dapm.idle_bias_off = true;
  411. snd_soc_card_set_drvdata(card, cdev);
  412. ret = devm_snd_soc_register_card(dev, card);
  413. if (ret < 0) {
  414. debugfs_remove(priv->dfs_points);
  415. debugfs_remove(priv->dfs_points_remove);
  416. dev_err(dev, "Probes card register failed %d\n", ret);
  417. return ret;
  418. }
  419. /* enable runtime PM */
  420. pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
  421. pm_runtime_use_autosuspend(dev);
  422. pm_runtime_enable(dev);
  423. pm_runtime_mark_last_busy(dev);
  424. pm_runtime_idle(dev);
  425. return 0;
  426. }
  427. static void sof_probes_client_remove(struct auxiliary_device *auxdev)
  428. {
  429. struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
  430. struct sof_probes_priv *priv = cdev->data;
  431. if (!sof_probes_enabled)
  432. return;
  433. pm_runtime_disable(&auxdev->dev);
  434. debugfs_remove(priv->dfs_points);
  435. debugfs_remove(priv->dfs_points_remove);
  436. }
  437. static const struct auxiliary_device_id sof_probes_client_id_table[] = {
  438. { .name = "snd_sof.hda-probes", },
  439. { .name = "snd_sof.acp-probes", },
  440. {},
  441. };
  442. MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
  443. /* driver name will be set based on KBUILD_MODNAME */
  444. static struct auxiliary_driver sof_probes_client_drv = {
  445. .probe = sof_probes_client_probe,
  446. .remove = sof_probes_client_remove,
  447. .id_table = sof_probes_client_id_table,
  448. };
  449. module_auxiliary_driver(sof_probes_client_drv);
  450. MODULE_LICENSE("GPL v2");
  451. MODULE_DESCRIPTION("SOF Probes Client Driver");
  452. MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);