sof-client-probes-ipc4.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. //
  3. // Copyright(c) 2019-2022 Intel Corporation
  4. //
  5. // Author: Jyri Sarha <jyri.sarha@intel.com>
  6. //
  7. #include <sound/soc.h>
  8. #include <sound/sof/ipc4/header.h>
  9. #include <uapi/sound/sof/header.h>
  10. #include "sof-audio.h"
  11. #include "ipc4-priv.h"
  12. #include "sof-client.h"
  13. #include "sof-client-probes.h"
  14. enum sof_ipc4_dma_type {
  15. SOF_IPC4_DMA_HDA_HOST_OUTPUT = 0,
  16. SOF_IPC4_DMA_HDA_HOST_INPUT = 1,
  17. SOF_IPC4_DMA_HDA_LINK_OUTPUT = 8,
  18. SOF_IPC4_DMA_HDA_LINK_INPUT = 9,
  19. SOF_IPC4_DMA_DMIC_LINK_INPUT = 11,
  20. SOF_IPC4_DMA_I2S_LINK_OUTPUT = 12,
  21. SOF_IPC4_DMA_I2S_LINK_INPUT = 13,
  22. };
  23. enum sof_ipc4_probe_runtime_param {
  24. SOF_IPC4_PROBE_INJECTION_DMA = 1,
  25. SOF_IPC4_PROBE_INJECTION_DMA_DETACH,
  26. SOF_IPC4_PROBE_POINTS,
  27. SOF_IPC4_PROBE_POINTS_DISCONNECT,
  28. SOF_IPC4_PROBE_POINTS_AVAILABLE,
  29. };
  30. struct sof_ipc4_probe_gtw_cfg {
  31. u32 node_id;
  32. u32 dma_buffer_size;
  33. } __packed __aligned(4);
  34. #define SOF_IPC4_PROBE_NODE_ID_INDEX(x) ((x) & GENMASK(7, 0))
  35. #define SOF_IPC4_PROBE_NODE_ID_TYPE(x) (((x) << 8) & GENMASK(12, 8))
  36. struct sof_ipc4_probe_cfg {
  37. struct sof_ipc4_base_module_cfg base;
  38. struct sof_ipc4_probe_gtw_cfg gtw_cfg;
  39. } __packed __aligned(4);
  40. enum sof_ipc4_probe_type {
  41. SOF_IPC4_PROBE_TYPE_INPUT = 0,
  42. SOF_IPC4_PROBE_TYPE_OUTPUT,
  43. SOF_IPC4_PROBE_TYPE_INTERNAL
  44. };
  45. #define SOF_IPC4_PROBE_TYPE_SHIFT 24
  46. #define SOF_IPC4_PROBE_TYPE_MASK GENMASK(25, 24)
  47. #define SOF_IPC4_PROBE_TYPE_GET(x) (((x) & SOF_IPC4_PROBE_TYPE_MASK) \
  48. >> SOF_IPC4_PROBE_TYPE_SHIFT)
  49. #define SOF_IPC4_PROBE_IDX_SHIFT 26
  50. #define SOF_IPC4_PROBE_IDX_MASK GENMASK(31, 26)
  51. #define SOF_IPC4_PROBE_IDX_GET(x) (((x) & SOF_IPC4_PROBE_IDX_MASK) \
  52. >> SOF_IPC4_PROBE_IDX_SHIFT)
  53. struct sof_ipc4_probe_point {
  54. u32 point_id;
  55. u32 purpose;
  56. u32 stream_tag;
  57. } __packed __aligned(4);
  58. struct sof_ipc4_probe_info {
  59. unsigned int num_elems;
  60. DECLARE_FLEX_ARRAY(struct sof_ipc4_probe_point, points);
  61. } __packed;
  62. #define INVALID_PIPELINE_ID 0xFF
  63. static const char *sof_probe_ipc4_type_string(u32 type)
  64. {
  65. switch (type) {
  66. case SOF_IPC4_PROBE_TYPE_INPUT:
  67. return "input";
  68. case SOF_IPC4_PROBE_TYPE_OUTPUT:
  69. return "output";
  70. case SOF_IPC4_PROBE_TYPE_INTERNAL:
  71. return "internal";
  72. default:
  73. return "UNKNOWN";
  74. }
  75. }
  76. /**
  77. * sof_ipc4_probe_get_module_info - Get IPC4 module info for probe module
  78. * @cdev: SOF client device
  79. * @return: Pointer to IPC4 probe module info
  80. *
  81. * Look up the IPC4 probe module info based on the hard coded uuid and
  82. * store the value for the future calls.
  83. */
  84. static struct sof_man4_module *sof_ipc4_probe_get_module_info(struct sof_client_dev *cdev)
  85. {
  86. struct sof_probes_priv *priv = cdev->data;
  87. struct device *dev = &cdev->auxdev.dev;
  88. static const guid_t probe_uuid =
  89. GUID_INIT(0x7CAD0808, 0xAB10, 0xCD23,
  90. 0xEF, 0x45, 0x12, 0xAB, 0x34, 0xCD, 0x56, 0xEF);
  91. if (!priv->ipc_priv) {
  92. struct sof_ipc4_fw_module *fw_module =
  93. sof_client_ipc4_find_module(cdev, &probe_uuid);
  94. if (!fw_module) {
  95. dev_err(dev, "%s: no matching uuid found", __func__);
  96. return NULL;
  97. }
  98. priv->ipc_priv = &fw_module->man4_module_entry;
  99. }
  100. return (struct sof_man4_module *)priv->ipc_priv;
  101. }
  102. /**
  103. * ipc4_probes_init - initialize data probing
  104. * @cdev: SOF client device
  105. * @stream_tag: Extractor stream tag
  106. * @buffer_size: DMA buffer size to set for extractor
  107. * @return: 0 on success, negative error code on error
  108. *
  109. * Host chooses whether extraction is supported or not by providing
  110. * valid stream tag to DSP. Once specified, stream described by that
  111. * tag will be tied to DSP for extraction for the entire lifetime of
  112. * probe.
  113. *
  114. * Probing is initialized only once and each INIT request must be
  115. * matched by DEINIT call.
  116. */
  117. static int ipc4_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
  118. size_t buffer_size)
  119. {
  120. struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
  121. struct sof_ipc4_msg msg;
  122. struct sof_ipc4_probe_cfg cfg;
  123. if (!mentry)
  124. return -ENODEV;
  125. memset(&cfg, '\0', sizeof(cfg));
  126. cfg.gtw_cfg.node_id = SOF_IPC4_PROBE_NODE_ID_INDEX(stream_tag - 1) |
  127. SOF_IPC4_PROBE_NODE_ID_TYPE(SOF_IPC4_DMA_HDA_HOST_INPUT);
  128. cfg.gtw_cfg.dma_buffer_size = buffer_size;
  129. msg.primary = mentry->id;
  130. msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE);
  131. msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  132. msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
  133. msg.extension = SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(INVALID_PIPELINE_ID);
  134. msg.extension |= SOF_IPC4_MOD_EXT_CORE_ID(0);
  135. msg.extension |= SOF_IPC4_MOD_EXT_PARAM_SIZE(sizeof(cfg) / sizeof(uint32_t));
  136. msg.data_size = sizeof(cfg);
  137. msg.data_ptr = &cfg;
  138. return sof_client_ipc_tx_message_no_reply(cdev, &msg);
  139. }
  140. /**
  141. * ipc4_probes_deinit - cleanup after data probing
  142. * @cdev: SOF client device
  143. * @return: 0 on success, negative error code on error
  144. *
  145. * Host sends DEINIT request to free previously initialized probe
  146. * on DSP side once it is no longer needed. DEINIT only when there
  147. * are no probes connected and with all injectors detached.
  148. */
  149. static int ipc4_probes_deinit(struct sof_client_dev *cdev)
  150. {
  151. struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
  152. struct sof_ipc4_msg msg;
  153. if (!mentry)
  154. return -ENODEV;
  155. msg.primary = mentry->id;
  156. msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_DELETE_INSTANCE);
  157. msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  158. msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
  159. msg.extension = SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(INVALID_PIPELINE_ID);
  160. msg.extension |= SOF_IPC4_MOD_EXT_CORE_ID(0);
  161. msg.data_size = 0;
  162. msg.data_ptr = NULL;
  163. return sof_client_ipc_tx_message_no_reply(cdev, &msg);
  164. }
  165. /**
  166. * ipc4_probes_points_info - retrieve list of probe points
  167. * @cdev: SOF client device
  168. * @desc: Returned list of active probes
  169. * @num_desc: Returned count of active probes
  170. * @type: Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
  171. * @return: 0 on success, negative error code on error
  172. *
  173. * Returns list if active probe points if type is
  174. * PROBES_INFO_ACTIVE_PROBES, or list of all available probe points if
  175. * type is PROBES_INFO_AVAILABE_PROBES.
  176. */
  177. static int ipc4_probes_points_info(struct sof_client_dev *cdev,
  178. struct sof_probe_point_desc **desc,
  179. size_t *num_desc,
  180. enum sof_probe_info_type type)
  181. {
  182. struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
  183. struct device *dev = &cdev->auxdev.dev;
  184. struct sof_ipc4_probe_info *info;
  185. struct sof_ipc4_msg msg;
  186. u32 param_id;
  187. int i, ret;
  188. if (!mentry)
  189. return -ENODEV;
  190. switch (type) {
  191. case PROBES_INFO_ACTIVE_PROBES:
  192. param_id = SOF_IPC4_PROBE_POINTS;
  193. break;
  194. case PROBES_INFO_AVAILABE_PROBES:
  195. param_id = SOF_IPC4_PROBE_POINTS_AVAILABLE;
  196. break;
  197. default:
  198. dev_err(dev, "%s: info type %u not supported", __func__, type);
  199. return -EOPNOTSUPP;
  200. }
  201. msg.primary = mentry->id;
  202. msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  203. msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
  204. msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(param_id);
  205. msg.data_size = sof_client_get_ipc_max_payload_size(cdev);
  206. msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL);
  207. if (!msg.data_ptr)
  208. return -ENOMEM;
  209. ret = sof_client_ipc_set_get_data(cdev, &msg, false);
  210. if (ret) {
  211. kfree(msg.data_ptr);
  212. return ret;
  213. }
  214. info = msg.data_ptr;
  215. *num_desc = info->num_elems;
  216. dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc);
  217. *desc = kzalloc(*num_desc * sizeof(**desc), GFP_KERNEL);
  218. if (!*desc) {
  219. kfree(msg.data_ptr);
  220. return -ENOMEM;
  221. }
  222. for (i = 0; i < *num_desc; i++) {
  223. (*desc)[i].buffer_id = info->points[i].point_id;
  224. (*desc)[i].purpose = info->points[i].purpose;
  225. (*desc)[i].stream_tag = info->points[i].stream_tag;
  226. }
  227. kfree(msg.data_ptr);
  228. return 0;
  229. }
  230. /**
  231. * ipc4_probes_point_print - Human readable print of probe point descriptor
  232. * @cdev: SOF client device
  233. * @buf: Buffer to print to
  234. * @size: Available bytes in buffer
  235. * @desc: Describes the probe point to print
  236. * @return: Number of bytes printed or an error code (snprintf return value)
  237. */
  238. static int ipc4_probes_point_print(struct sof_client_dev *cdev, char *buf, size_t size,
  239. struct sof_probe_point_desc *desc)
  240. {
  241. struct device *dev = &cdev->auxdev.dev;
  242. struct snd_sof_widget *swidget;
  243. int ret;
  244. swidget = sof_client_ipc4_find_swidget_by_id(cdev, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
  245. SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
  246. if (!swidget)
  247. dev_err(dev, "%s: Failed to find widget for module %lu.%lu\n",
  248. __func__, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
  249. SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
  250. ret = scnprintf(buf, size, "%#x,%#x,%#x\t%s %s buf idx %lu %s\n",
  251. desc->buffer_id, desc->purpose, desc->stream_tag,
  252. swidget ? swidget->widget->name : "<unknown>",
  253. sof_probe_ipc4_type_string(SOF_IPC4_PROBE_TYPE_GET(desc->buffer_id)),
  254. SOF_IPC4_PROBE_IDX_GET(desc->buffer_id),
  255. desc->stream_tag ? "(connected)" : "");
  256. return ret;
  257. }
  258. /**
  259. * ipc4_probes_points_add - connect specified probes
  260. * @cdev: SOF client device
  261. * @desc: List of probe points to connect
  262. * @num_desc: Number of elements in @desc
  263. * @return: 0 on success, negative error code on error
  264. *
  265. * Translates the generic probe point presentation to an IPC4
  266. * message to dynamically connect the provided set of endpoints.
  267. */
  268. static int ipc4_probes_points_add(struct sof_client_dev *cdev,
  269. struct sof_probe_point_desc *desc,
  270. size_t num_desc)
  271. {
  272. struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
  273. struct sof_ipc4_probe_point *points;
  274. struct sof_ipc4_msg msg;
  275. int i, ret;
  276. if (!mentry)
  277. return -EOPNOTSUPP;
  278. /* The sof_probe_point_desc and sof_ipc4_probe_point structs
  279. * are of same size and even the integers are the same in the
  280. * same order, and similar meaning, but since there is no
  281. * performance issue I wrote the conversion explicitly open for
  282. * future development.
  283. */
  284. points = kzalloc_objs(*points, num_desc);
  285. if (!points)
  286. return -ENOMEM;
  287. for (i = 0; i < num_desc; i++) {
  288. points[i].point_id = desc[i].buffer_id;
  289. points[i].purpose = desc[i].purpose;
  290. points[i].stream_tag = desc[i].stream_tag;
  291. }
  292. msg.primary = mentry->id;
  293. msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  294. msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
  295. msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_PROBE_POINTS);
  296. msg.data_size = sizeof(*points) * num_desc;
  297. msg.data_ptr = points;
  298. ret = sof_client_ipc_set_get_data(cdev, &msg, true);
  299. kfree(points);
  300. return ret;
  301. }
  302. /**
  303. * ipc4_probes_points_remove - disconnect specified probes
  304. * @cdev: SOF client device
  305. * @buffer_id: List of probe points to disconnect
  306. * @num_buffer_id: Number of elements in @desc
  307. * @return: 0 on success, negative error code on error
  308. *
  309. * Converts the generic buffer_id to IPC4 probe_point_id and remove
  310. * the probe points with an IPC4 for message.
  311. */
  312. static int ipc4_probes_points_remove(struct sof_client_dev *cdev,
  313. unsigned int *buffer_id, size_t num_buffer_id)
  314. {
  315. struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
  316. struct sof_ipc4_msg msg;
  317. u32 *probe_point_ids;
  318. int i, ret;
  319. if (!mentry)
  320. return -ENODEV;
  321. probe_point_ids = kcalloc(num_buffer_id, sizeof(*probe_point_ids),
  322. GFP_KERNEL);
  323. if (!probe_point_ids)
  324. return -ENOMEM;
  325. for (i = 0; i < num_buffer_id; i++)
  326. probe_point_ids[i] = buffer_id[i];
  327. msg.primary = mentry->id;
  328. msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  329. msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
  330. msg.extension =
  331. SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_PROBE_POINTS_DISCONNECT);
  332. msg.data_size = num_buffer_id * sizeof(*probe_point_ids);
  333. msg.data_ptr = probe_point_ids;
  334. ret = sof_client_ipc_set_get_data(cdev, &msg, true);
  335. kfree(probe_point_ids);
  336. return ret;
  337. }
  338. const struct sof_probes_ipc_ops ipc4_probe_ops = {
  339. .init = ipc4_probes_init,
  340. .deinit = ipc4_probes_deinit,
  341. .points_info = ipc4_probes_points_info,
  342. .point_print = ipc4_probes_point_print,
  343. .points_add = ipc4_probes_points_add,
  344. .points_remove = ipc4_probes_points_remove,
  345. };