18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2019-2020 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Cezary Rojewski <cezary.rojewski@intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "sof-priv.h" 128c2ecf20Sopenharmony_ci#include "probe.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/** 158c2ecf20Sopenharmony_ci * sof_ipc_probe_init - initialize data probing 168c2ecf20Sopenharmony_ci * @sdev: SOF sound device 178c2ecf20Sopenharmony_ci * @stream_tag: Extractor stream tag 188c2ecf20Sopenharmony_ci * @buffer_size: DMA buffer size to set for extractor 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Host chooses whether extraction is supported or not by providing 218c2ecf20Sopenharmony_ci * valid stream tag to DSP. Once specified, stream described by that 228c2ecf20Sopenharmony_ci * tag will be tied to DSP for extraction for the entire lifetime of 238c2ecf20Sopenharmony_ci * probe. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Probing is initialized only once and each INIT request must be 268c2ecf20Sopenharmony_ci * matched by DEINIT call. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ciint sof_ipc_probe_init(struct snd_sof_dev *sdev, 298c2ecf20Sopenharmony_ci u32 stream_tag, size_t buffer_size) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct sof_ipc_probe_dma_add_params *msg; 328c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 338c2ecf20Sopenharmony_ci size_t size = struct_size(msg, dma, 1); 348c2ecf20Sopenharmony_ci int ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci msg = kmalloc(size, GFP_KERNEL); 378c2ecf20Sopenharmony_ci if (!msg) 388c2ecf20Sopenharmony_ci return -ENOMEM; 398c2ecf20Sopenharmony_ci msg->hdr.size = size; 408c2ecf20Sopenharmony_ci msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; 418c2ecf20Sopenharmony_ci msg->num_elems = 1; 428c2ecf20Sopenharmony_ci msg->dma[0].stream_tag = stream_tag; 438c2ecf20Sopenharmony_ci msg->dma[0].dma_buffer_size = buffer_size; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 468c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 478c2ecf20Sopenharmony_ci kfree(msg); 488c2ecf20Sopenharmony_ci return ret; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_init); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * sof_ipc_probe_deinit - cleanup after data probing 548c2ecf20Sopenharmony_ci * @sdev: SOF sound device 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * Host sends DEINIT request to free previously initialized probe 578c2ecf20Sopenharmony_ci * on DSP side once it is no longer needed. DEINIT only when there 588c2ecf20Sopenharmony_ci * are no probes connected and with all injectors detached. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ciint sof_ipc_probe_deinit(struct snd_sof_dev *sdev) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct sof_ipc_cmd_hdr msg; 638c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci msg.size = sizeof(msg); 668c2ecf20Sopenharmony_ci msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, 698c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_deinit); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, 748c2ecf20Sopenharmony_ci void **params, size_t *num_params) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct sof_ipc_probe_info_params msg = {{{0}}}; 778c2ecf20Sopenharmony_ci struct sof_ipc_probe_info_params *reply; 788c2ecf20Sopenharmony_ci size_t bytes; 798c2ecf20Sopenharmony_ci int ret; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci *params = NULL; 828c2ecf20Sopenharmony_ci *num_params = 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 858c2ecf20Sopenharmony_ci if (!reply) 868c2ecf20Sopenharmony_ci return -ENOMEM; 878c2ecf20Sopenharmony_ci msg.rhdr.hdr.size = sizeof(msg); 888c2ecf20Sopenharmony_ci msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, 918c2ecf20Sopenharmony_ci msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE); 928c2ecf20Sopenharmony_ci if (ret < 0 || reply->rhdr.error < 0) 938c2ecf20Sopenharmony_ci goto exit; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (!reply->num_elems) 968c2ecf20Sopenharmony_ci goto exit; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (cmd == SOF_IPC_PROBE_DMA_INFO) 998c2ecf20Sopenharmony_ci bytes = sizeof(reply->dma[0]); 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci bytes = sizeof(reply->desc[0]); 1028c2ecf20Sopenharmony_ci bytes *= reply->num_elems; 1038c2ecf20Sopenharmony_ci *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); 1048c2ecf20Sopenharmony_ci if (!*params) { 1058c2ecf20Sopenharmony_ci ret = -ENOMEM; 1068c2ecf20Sopenharmony_ci goto exit; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci *num_params = reply->num_elems; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciexit: 1118c2ecf20Sopenharmony_ci kfree(reply); 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * sof_ipc_probe_dma_info - retrieve list of active injection dmas 1178c2ecf20Sopenharmony_ci * @sdev: SOF sound device 1188c2ecf20Sopenharmony_ci * @dma: Returned list of active dmas 1198c2ecf20Sopenharmony_ci * @num_dma: Returned count of active dmas 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * Host sends DMA_INFO request to obtain list of injection dmas it 1228c2ecf20Sopenharmony_ci * can use to transfer data over with. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Note that list contains only injection dmas as there is only one 1258c2ecf20Sopenharmony_ci * extractor (dma) and it is always assigned on probing init. 1268c2ecf20Sopenharmony_ci * DSP knows exactly where data from extraction probes is going to, 1278c2ecf20Sopenharmony_ci * which is not the case for injection where multiple streams 1288c2ecf20Sopenharmony_ci * could be engaged. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ciint sof_ipc_probe_dma_info(struct snd_sof_dev *sdev, 1318c2ecf20Sopenharmony_ci struct sof_probe_dma **dma, size_t *num_dma) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_DMA_INFO, 1348c2ecf20Sopenharmony_ci (void **)dma, num_dma); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_dma_info); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * sof_ipc_probe_dma_add - attach to specified dmas 1408c2ecf20Sopenharmony_ci * @sdev: SOF sound device 1418c2ecf20Sopenharmony_ci * @dma: List of streams (dmas) to attach to 1428c2ecf20Sopenharmony_ci * @num_dma: Number of elements in @dma 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * Contrary to extraction, injection streams are never assigned 1458c2ecf20Sopenharmony_ci * on init. Before attempting any data injection, host is responsible 1468c2ecf20Sopenharmony_ci * for specifying streams which will be later used to transfer data 1478c2ecf20Sopenharmony_ci * to connected probe points. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ciint sof_ipc_probe_dma_add(struct snd_sof_dev *sdev, 1508c2ecf20Sopenharmony_ci struct sof_probe_dma *dma, size_t num_dma) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct sof_ipc_probe_dma_add_params *msg; 1538c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 1548c2ecf20Sopenharmony_ci size_t size = struct_size(msg, dma, num_dma); 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci msg = kmalloc(size, GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!msg) 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci msg->hdr.size = size; 1618c2ecf20Sopenharmony_ci msg->num_elems = num_dma; 1628c2ecf20Sopenharmony_ci msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_ADD; 1638c2ecf20Sopenharmony_ci memcpy(&msg->dma[0], dma, size - sizeof(*msg)); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 1668c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 1678c2ecf20Sopenharmony_ci kfree(msg); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_dma_add); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/** 1738c2ecf20Sopenharmony_ci * sof_ipc_probe_dma_remove - detach from specified dmas 1748c2ecf20Sopenharmony_ci * @sdev: SOF sound device 1758c2ecf20Sopenharmony_ci * @stream_tag: List of stream tags to detach from 1768c2ecf20Sopenharmony_ci * @num_stream_tag: Number of elements in @stream_tag 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Host sends DMA_REMOVE request to free previously attached stream 1798c2ecf20Sopenharmony_ci * from being occupied for injection. Each detach operation should 1808c2ecf20Sopenharmony_ci * match equivalent DMA_ADD. Detach only when all probes tied to 1818c2ecf20Sopenharmony_ci * given stream have been disconnected. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ciint sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev, 1848c2ecf20Sopenharmony_ci unsigned int *stream_tag, size_t num_stream_tag) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct sof_ipc_probe_dma_remove_params *msg; 1878c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 1888c2ecf20Sopenharmony_ci size_t size = struct_size(msg, stream_tag, num_stream_tag); 1898c2ecf20Sopenharmony_ci int ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci msg = kmalloc(size, GFP_KERNEL); 1928c2ecf20Sopenharmony_ci if (!msg) 1938c2ecf20Sopenharmony_ci return -ENOMEM; 1948c2ecf20Sopenharmony_ci msg->hdr.size = size; 1958c2ecf20Sopenharmony_ci msg->num_elems = num_stream_tag; 1968c2ecf20Sopenharmony_ci msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_REMOVE; 1978c2ecf20Sopenharmony_ci memcpy(&msg->stream_tag[0], stream_tag, size - sizeof(*msg)); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 2008c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 2018c2ecf20Sopenharmony_ci kfree(msg); 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_dma_remove); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/** 2078c2ecf20Sopenharmony_ci * sof_ipc_probe_points_info - retrieve list of active probe points 2088c2ecf20Sopenharmony_ci * @sdev: SOF sound device 2098c2ecf20Sopenharmony_ci * @desc: Returned list of active probes 2108c2ecf20Sopenharmony_ci * @num_desc: Returned count of active probes 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * Host sends PROBE_POINT_INFO request to obtain list of active probe 2138c2ecf20Sopenharmony_ci * points, valid for disconnection when given probe is no longer 2148c2ecf20Sopenharmony_ci * required. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ciint sof_ipc_probe_points_info(struct snd_sof_dev *sdev, 2178c2ecf20Sopenharmony_ci struct sof_probe_point_desc **desc, size_t *num_desc) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, 2208c2ecf20Sopenharmony_ci (void **)desc, num_desc); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_points_info); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * sof_ipc_probe_points_add - connect specified probes 2268c2ecf20Sopenharmony_ci * @sdev: SOF sound device 2278c2ecf20Sopenharmony_ci * @desc: List of probe points to connect 2288c2ecf20Sopenharmony_ci * @num_desc: Number of elements in @desc 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Dynamically connects to provided set of endpoints. Immediately 2318c2ecf20Sopenharmony_ci * after connection is established, host must be prepared to 2328c2ecf20Sopenharmony_ci * transfer data from or to target stream given the probing purpose. 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * Each probe point should be removed using PROBE_POINT_REMOVE 2358c2ecf20Sopenharmony_ci * request when no longer needed. 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ciint sof_ipc_probe_points_add(struct snd_sof_dev *sdev, 2388c2ecf20Sopenharmony_ci struct sof_probe_point_desc *desc, size_t num_desc) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct sof_ipc_probe_point_add_params *msg; 2418c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 2428c2ecf20Sopenharmony_ci size_t size = struct_size(msg, desc, num_desc); 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci msg = kmalloc(size, GFP_KERNEL); 2468c2ecf20Sopenharmony_ci if (!msg) 2478c2ecf20Sopenharmony_ci return -ENOMEM; 2488c2ecf20Sopenharmony_ci msg->hdr.size = size; 2498c2ecf20Sopenharmony_ci msg->num_elems = num_desc; 2508c2ecf20Sopenharmony_ci msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; 2518c2ecf20Sopenharmony_ci memcpy(&msg->desc[0], desc, size - sizeof(*msg)); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 2548c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 2558c2ecf20Sopenharmony_ci kfree(msg); 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_points_add); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * sof_ipc_probe_points_remove - disconnect specified probes 2628c2ecf20Sopenharmony_ci * @sdev: SOF sound device 2638c2ecf20Sopenharmony_ci * @buffer_id: List of probe points to disconnect 2648c2ecf20Sopenharmony_ci * @num_buffer_id: Number of elements in @desc 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * Removes previously connected probes from list of active probe 2678c2ecf20Sopenharmony_ci * points and frees all resources on DSP side. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ciint sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, 2708c2ecf20Sopenharmony_ci unsigned int *buffer_id, size_t num_buffer_id) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct sof_ipc_probe_point_remove_params *msg; 2738c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 2748c2ecf20Sopenharmony_ci size_t size = struct_size(msg, buffer_id, num_buffer_id); 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci msg = kmalloc(size, GFP_KERNEL); 2788c2ecf20Sopenharmony_ci if (!msg) 2798c2ecf20Sopenharmony_ci return -ENOMEM; 2808c2ecf20Sopenharmony_ci msg->hdr.size = size; 2818c2ecf20Sopenharmony_ci msg->num_elems = num_buffer_id; 2828c2ecf20Sopenharmony_ci msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; 2838c2ecf20Sopenharmony_ci memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, 2868c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 2878c2ecf20Sopenharmony_ci kfree(msg); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_probe_points_remove); 291