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