162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright(c) 2019-2022 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Author: Jyri Sarha <jyri.sarha@intel.com> 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <sound/soc.h> 962306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h> 1062306a36Sopenharmony_ci#include <uapi/sound/sof/header.h> 1162306a36Sopenharmony_ci#include "sof-priv.h" 1262306a36Sopenharmony_ci#include "ipc4-priv.h" 1362306a36Sopenharmony_ci#include "sof-client.h" 1462306a36Sopenharmony_ci#include "sof-client-probes.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cienum sof_ipc4_dma_type { 1762306a36Sopenharmony_ci SOF_IPC4_DMA_HDA_HOST_OUTPUT = 0, 1862306a36Sopenharmony_ci SOF_IPC4_DMA_HDA_HOST_INPUT = 1, 1962306a36Sopenharmony_ci SOF_IPC4_DMA_HDA_LINK_OUTPUT = 8, 2062306a36Sopenharmony_ci SOF_IPC4_DMA_HDA_LINK_INPUT = 9, 2162306a36Sopenharmony_ci SOF_IPC4_DMA_DMIC_LINK_INPUT = 11, 2262306a36Sopenharmony_ci SOF_IPC4_DMA_I2S_LINK_OUTPUT = 12, 2362306a36Sopenharmony_ci SOF_IPC4_DMA_I2S_LINK_INPUT = 13, 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cienum sof_ipc4_probe_runtime_param { 2762306a36Sopenharmony_ci SOF_IPC4_PROBE_INJECTION_DMA = 1, 2862306a36Sopenharmony_ci SOF_IPC4_PROBE_INJECTION_DMA_DETACH, 2962306a36Sopenharmony_ci SOF_IPC4_PROBE_POINTS, 3062306a36Sopenharmony_ci SOF_IPC4_PROBE_POINTS_DISCONNECT, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct sof_ipc4_probe_gtw_cfg { 3462306a36Sopenharmony_ci u32 node_id; 3562306a36Sopenharmony_ci u32 dma_buffer_size; 3662306a36Sopenharmony_ci} __packed __aligned(4); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define SOF_IPC4_PROBE_NODE_ID_INDEX(x) ((x) & GENMASK(7, 0)) 3962306a36Sopenharmony_ci#define SOF_IPC4_PROBE_NODE_ID_TYPE(x) (((x) << 8) & GENMASK(12, 8)) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct sof_ipc4_probe_cfg { 4262306a36Sopenharmony_ci struct sof_ipc4_base_module_cfg base; 4362306a36Sopenharmony_ci struct sof_ipc4_probe_gtw_cfg gtw_cfg; 4462306a36Sopenharmony_ci} __packed __aligned(4); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum sof_ipc4_probe_type { 4762306a36Sopenharmony_ci SOF_IPC4_PROBE_TYPE_INPUT = 0, 4862306a36Sopenharmony_ci SOF_IPC4_PROBE_TYPE_OUTPUT, 4962306a36Sopenharmony_ci SOF_IPC4_PROBE_TYPE_INTERNAL 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct sof_ipc4_probe_point { 5362306a36Sopenharmony_ci u32 point_id; 5462306a36Sopenharmony_ci u32 purpose; 5562306a36Sopenharmony_ci u32 stream_tag; 5662306a36Sopenharmony_ci} __packed __aligned(4); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define INVALID_PIPELINE_ID 0xFF 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/** 6162306a36Sopenharmony_ci * sof_ipc4_probe_get_module_info - Get IPC4 module info for probe module 6262306a36Sopenharmony_ci * @cdev: SOF client device 6362306a36Sopenharmony_ci * @return: Pointer to IPC4 probe module info 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * Look up the IPC4 probe module info based on the hard coded uuid and 6662306a36Sopenharmony_ci * store the value for the future calls. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic struct sof_man4_module *sof_ipc4_probe_get_module_info(struct sof_client_dev *cdev) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct sof_probes_priv *priv = cdev->data; 7162306a36Sopenharmony_ci struct device *dev = &cdev->auxdev.dev; 7262306a36Sopenharmony_ci static const guid_t probe_uuid = 7362306a36Sopenharmony_ci GUID_INIT(0x7CAD0808, 0xAB10, 0xCD23, 7462306a36Sopenharmony_ci 0xEF, 0x45, 0x12, 0xAB, 0x34, 0xCD, 0x56, 0xEF); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (!priv->ipc_priv) { 7762306a36Sopenharmony_ci struct sof_ipc4_fw_module *fw_module = 7862306a36Sopenharmony_ci sof_client_ipc4_find_module(cdev, &probe_uuid); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (!fw_module) { 8162306a36Sopenharmony_ci dev_err(dev, "%s: no matching uuid found", __func__); 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci priv->ipc_priv = &fw_module->man4_module_entry; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return (struct sof_man4_module *)priv->ipc_priv; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * ipc4_probes_init - initialize data probing 9362306a36Sopenharmony_ci * @cdev: SOF client device 9462306a36Sopenharmony_ci * @stream_tag: Extractor stream tag 9562306a36Sopenharmony_ci * @buffer_size: DMA buffer size to set for extractor 9662306a36Sopenharmony_ci * @return: 0 on success, negative error code on error 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * Host chooses whether extraction is supported or not by providing 9962306a36Sopenharmony_ci * valid stream tag to DSP. Once specified, stream described by that 10062306a36Sopenharmony_ci * tag will be tied to DSP for extraction for the entire lifetime of 10162306a36Sopenharmony_ci * probe. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Probing is initialized only once and each INIT request must be 10462306a36Sopenharmony_ci * matched by DEINIT call. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistatic int ipc4_probes_init(struct sof_client_dev *cdev, u32 stream_tag, 10762306a36Sopenharmony_ci size_t buffer_size) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 11062306a36Sopenharmony_ci struct sof_ipc4_msg msg; 11162306a36Sopenharmony_ci struct sof_ipc4_probe_cfg cfg; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!mentry) 11462306a36Sopenharmony_ci return -ENODEV; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci memset(&cfg, '\0', sizeof(cfg)); 11762306a36Sopenharmony_ci cfg.gtw_cfg.node_id = SOF_IPC4_PROBE_NODE_ID_INDEX(stream_tag - 1) | 11862306a36Sopenharmony_ci SOF_IPC4_PROBE_NODE_ID_TYPE(SOF_IPC4_DMA_HDA_HOST_INPUT); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci cfg.gtw_cfg.dma_buffer_size = buffer_size; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci msg.primary = mentry->id; 12362306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE); 12462306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 12562306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 12662306a36Sopenharmony_ci msg.extension = SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(INVALID_PIPELINE_ID); 12762306a36Sopenharmony_ci msg.extension |= SOF_IPC4_MOD_EXT_CORE_ID(0); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci msg.data_size = sizeof(cfg); 13062306a36Sopenharmony_ci msg.data_ptr = &cfg; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return sof_client_ipc_tx_message_no_reply(cdev, &msg); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * ipc4_probes_deinit - cleanup after data probing 13762306a36Sopenharmony_ci * @cdev: SOF client device 13862306a36Sopenharmony_ci * @return: 0 on success, negative error code on error 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Host sends DEINIT request to free previously initialized probe 14162306a36Sopenharmony_ci * on DSP side once it is no longer needed. DEINIT only when there 14262306a36Sopenharmony_ci * are no probes connected and with all injectors detached. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic int ipc4_probes_deinit(struct sof_client_dev *cdev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 14762306a36Sopenharmony_ci struct sof_ipc4_msg msg; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!mentry) 15062306a36Sopenharmony_ci return -ENODEV; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci msg.primary = mentry->id; 15362306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_DELETE_INSTANCE); 15462306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 15562306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 15662306a36Sopenharmony_ci msg.extension = SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(INVALID_PIPELINE_ID); 15762306a36Sopenharmony_ci msg.extension |= SOF_IPC4_MOD_EXT_CORE_ID(0); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci msg.data_size = 0; 16062306a36Sopenharmony_ci msg.data_ptr = NULL; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return sof_client_ipc_tx_message_no_reply(cdev, &msg); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * ipc4_probes_points_info - retrieve list of active probe points 16762306a36Sopenharmony_ci * @cdev: SOF client device 16862306a36Sopenharmony_ci * @desc: Returned list of active probes 16962306a36Sopenharmony_ci * @num_desc: Returned count of active probes 17062306a36Sopenharmony_ci * @return: 0 on success, negative error code on error 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * Dummy implementation returning empty list of probes. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistatic int ipc4_probes_points_info(struct sof_client_dev *cdev, 17562306a36Sopenharmony_ci struct sof_probe_point_desc **desc, 17662306a36Sopenharmony_ci size_t *num_desc) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci /* TODO: Firmware side implementation needed first */ 17962306a36Sopenharmony_ci *desc = NULL; 18062306a36Sopenharmony_ci *num_desc = 0; 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/** 18562306a36Sopenharmony_ci * ipc4_probes_points_add - connect specified probes 18662306a36Sopenharmony_ci * @cdev: SOF client device 18762306a36Sopenharmony_ci * @desc: List of probe points to connect 18862306a36Sopenharmony_ci * @num_desc: Number of elements in @desc 18962306a36Sopenharmony_ci * @return: 0 on success, negative error code on error 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Translates the generic probe point presentation to an IPC4 19262306a36Sopenharmony_ci * message to dynamically connect the provided set of endpoints. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_cistatic int ipc4_probes_points_add(struct sof_client_dev *cdev, 19562306a36Sopenharmony_ci struct sof_probe_point_desc *desc, 19662306a36Sopenharmony_ci size_t num_desc) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 19962306a36Sopenharmony_ci struct sof_ipc4_probe_point *points; 20062306a36Sopenharmony_ci struct sof_ipc4_msg msg; 20162306a36Sopenharmony_ci int i, ret; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!mentry) 20462306a36Sopenharmony_ci return -ENODEV; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* The sof_probe_point_desc and sof_ipc4_probe_point structs 20762306a36Sopenharmony_ci * are of same size and even the integers are the same in the 20862306a36Sopenharmony_ci * same order, and similar meaning, but since there is no 20962306a36Sopenharmony_ci * performance issue I wrote the conversion explicitly open for 21062306a36Sopenharmony_ci * future development. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci points = kcalloc(num_desc, sizeof(*points), GFP_KERNEL); 21362306a36Sopenharmony_ci if (!points) 21462306a36Sopenharmony_ci return -ENOMEM; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for (i = 0; i < num_desc; i++) { 21762306a36Sopenharmony_ci points[i].point_id = desc[i].buffer_id; 21862306a36Sopenharmony_ci points[i].purpose = desc[i].purpose; 21962306a36Sopenharmony_ci points[i].stream_tag = desc[i].stream_tag; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci msg.primary = mentry->id; 22362306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 22462306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_PROBE_POINTS); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci msg.data_size = sizeof(*points) * num_desc; 22962306a36Sopenharmony_ci msg.data_ptr = points; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = sof_client_ipc_set_get_data(cdev, &msg, true); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci kfree(points); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/** 23962306a36Sopenharmony_ci * ipc4_probes_points_remove - disconnect specified probes 24062306a36Sopenharmony_ci * @cdev: SOF client device 24162306a36Sopenharmony_ci * @buffer_id: List of probe points to disconnect 24262306a36Sopenharmony_ci * @num_buffer_id: Number of elements in @desc 24362306a36Sopenharmony_ci * @return: 0 on success, negative error code on error 24462306a36Sopenharmony_ci * 24562306a36Sopenharmony_ci * Converts the generic buffer_id to IPC4 probe_point_id and remove 24662306a36Sopenharmony_ci * the probe points with an IPC4 for message. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistatic int ipc4_probes_points_remove(struct sof_client_dev *cdev, 24962306a36Sopenharmony_ci unsigned int *buffer_id, size_t num_buffer_id) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); 25262306a36Sopenharmony_ci struct sof_ipc4_msg msg; 25362306a36Sopenharmony_ci u32 *probe_point_ids; 25462306a36Sopenharmony_ci int i, ret; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!mentry) 25762306a36Sopenharmony_ci return -ENODEV; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci probe_point_ids = kcalloc(num_buffer_id, sizeof(*probe_point_ids), 26062306a36Sopenharmony_ci GFP_KERNEL); 26162306a36Sopenharmony_ci if (!probe_point_ids) 26262306a36Sopenharmony_ci return -ENOMEM; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (i = 0; i < num_buffer_id; i++) 26562306a36Sopenharmony_ci probe_point_ids[i] = buffer_id[i]; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci msg.primary = mentry->id; 26862306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 26962306a36Sopenharmony_ci msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci msg.extension = 27262306a36Sopenharmony_ci SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_PROBE_POINTS_DISCONNECT); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci msg.data_size = num_buffer_id * sizeof(*probe_point_ids); 27562306a36Sopenharmony_ci msg.data_ptr = probe_point_ids; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = sof_client_ipc_set_get_data(cdev, &msg, true); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci kfree(probe_point_ids); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciconst struct sof_probes_ipc_ops ipc4_probe_ops = { 28562306a36Sopenharmony_ci .init = ipc4_probes_init, 28662306a36Sopenharmony_ci .deinit = ipc4_probes_deinit, 28762306a36Sopenharmony_ci .points_info = ipc4_probes_points_info, 28862306a36Sopenharmony_ci .points_add = ipc4_probes_points_add, 28962306a36Sopenharmony_ci .points_remove = ipc4_probes_points_remove, 29062306a36Sopenharmony_ci}; 291