ASoC: SOF: sof-client-probes-ipc4: Query available

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

This series improves the IPC4 probes implementation by improving the
report quality and adds support for querying the available probes
on the firmware side.
This commit is contained in:
Mark Brown
2025-08-29 23:19:49 +01:00
7 changed files with 220 additions and 25 deletions

View File

@@ -326,10 +326,14 @@ struct sof_ipc4_base_module_cfg {
#define SOF_IPC4_MOD_INSTANCE_SHIFT 16
#define SOF_IPC4_MOD_INSTANCE_MASK GENMASK(23, 16)
#define SOF_IPC4_MOD_INSTANCE(x) ((x) << SOF_IPC4_MOD_INSTANCE_SHIFT)
#define SOF_IPC4_MOD_INSTANCE_GET(x) (((x) & SOF_IPC4_MOD_INSTANCE_MASK) \
>> SOF_IPC4_MOD_INSTANCE_SHIFT)
#define SOF_IPC4_MOD_ID_SHIFT 0
#define SOF_IPC4_MOD_ID_MASK GENMASK(15, 0)
#define SOF_IPC4_MOD_ID(x) ((x) << SOF_IPC4_MOD_ID_SHIFT)
#define SOF_IPC4_MOD_ID_GET(x) (((x) & SOF_IPC4_MOD_ID_MASK) \
>> SOF_IPC4_MOD_ID_SHIFT)
/* init module ipc msg */
#define SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT 0

View File

@@ -100,9 +100,11 @@ static int ipc3_probes_deinit(struct sof_client_dev *cdev)
}
static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
void **params, size_t *num_params)
void **params, size_t *num_params,
enum sof_probe_info_type type)
{
size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
struct device *dev = &cdev->auxdev.dev;
struct sof_ipc_probe_info_params msg = {{{0}}};
struct sof_ipc_probe_info_params *reply;
size_t bytes;
@@ -111,6 +113,11 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
*params = NULL;
*num_params = 0;
if (type != PROBES_INFO_ACTIVE_PROBES) {
dev_err(dev, "%s: info type %u not supported", __func__, type);
return -EOPNOTSUPP;
}
reply = kzalloc(max_msg_size, GFP_KERNEL);
if (!reply)
return -ENOMEM;
@@ -142,21 +149,25 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
}
/**
* ipc3_probes_points_info - retrieve list of active probe points
* ipc3_probes_points_info - retrieve list of probe points
* @cdev: SOF client device
* @desc: Returned list of active probes
* @num_desc: Returned count of active probes
* @type: Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
*
* Host sends PROBE_POINT_INFO request to obtain list of active probe
* points, valid for disconnection when given probe is no longer
* required.
* If type is PROBES_INFO_ACTIVE_PROBES, host sends PROBE_POINT_INFO
* request to obtain list of active probe points, valid for
* disconnection when given probe is no longer required.
*
* Type PROBES_INFO_AVAILABE_PROBES is not yet supported.
*/
static int ipc3_probes_points_info(struct sof_client_dev *cdev,
struct sof_probe_point_desc **desc,
size_t *num_desc)
size_t *num_desc,
enum sof_probe_info_type type)
{
return ipc3_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
(void **)desc, num_desc);
(void **)desc, num_desc, type);
}
/**

View File

@@ -8,7 +8,7 @@
#include <sound/soc.h>
#include <sound/sof/ipc4/header.h>
#include <uapi/sound/sof/header.h>
#include "sof-priv.h"
#include "sof-audio.h"
#include "ipc4-priv.h"
#include "sof-client.h"
#include "sof-client-probes.h"
@@ -28,6 +28,7 @@ enum sof_ipc4_probe_runtime_param {
SOF_IPC4_PROBE_INJECTION_DMA_DETACH,
SOF_IPC4_PROBE_POINTS,
SOF_IPC4_PROBE_POINTS_DISCONNECT,
SOF_IPC4_PROBE_POINTS_AVAILABLE,
};
struct sof_ipc4_probe_gtw_cfg {
@@ -49,14 +50,42 @@ enum sof_ipc4_probe_type {
SOF_IPC4_PROBE_TYPE_INTERNAL
};
#define SOF_IPC4_PROBE_TYPE_SHIFT 24
#define SOF_IPC4_PROBE_TYPE_MASK GENMASK(25, 24)
#define SOF_IPC4_PROBE_TYPE_GET(x) (((x) & SOF_IPC4_PROBE_TYPE_MASK) \
>> SOF_IPC4_PROBE_TYPE_SHIFT)
#define SOF_IPC4_PROBE_IDX_SHIFT 26
#define SOF_IPC4_PROBE_IDX_MASK GENMASK(31, 26)
#define SOF_IPC4_PROBE_IDX_GET(x) (((x) & SOF_IPC4_PROBE_IDX_MASK) \
>> SOF_IPC4_PROBE_IDX_SHIFT)
struct sof_ipc4_probe_point {
u32 point_id;
u32 purpose;
u32 stream_tag;
} __packed __aligned(4);
struct sof_ipc4_probe_info {
unsigned int num_elems;
DECLARE_FLEX_ARRAY(struct sof_ipc4_probe_point, points);
} __packed;
#define INVALID_PIPELINE_ID 0xFF
static const char *sof_probe_ipc4_type_string(u32 type)
{
switch (type) {
case SOF_IPC4_PROBE_TYPE_INPUT:
return "input";
case SOF_IPC4_PROBE_TYPE_OUTPUT:
return "output";
case SOF_IPC4_PROBE_TYPE_INTERNAL:
return "internal";
default:
return "UNKNOWN";
}
}
/**
* sof_ipc4_probe_get_module_info - Get IPC4 module info for probe module
* @cdev: SOF client device
@@ -164,24 +193,112 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev)
}
/**
* ipc4_probes_points_info - retrieve list of active probe points
* ipc4_probes_points_info - retrieve list of probe points
* @cdev: SOF client device
* @desc: Returned list of active probes
* @num_desc: Returned count of active probes
* @type: Either PROBES_INFO_ACTIVE_PROBES or PROBES_INFO_AVAILABE_PROBES
* @return: 0 on success, negative error code on error
*
* Dummy implementation returning empty list of probes.
* Returns list if active probe points if type is
* PROBES_INFO_ACTIVE_PROBES, or list of all available probe points if
* type is PROBES_INFO_AVAILABE_PROBES.
*/
static int ipc4_probes_points_info(struct sof_client_dev *cdev,
struct sof_probe_point_desc **desc,
size_t *num_desc)
size_t *num_desc,
enum sof_probe_info_type type)
{
/* TODO: Firmware side implementation needed first */
*desc = NULL;
*num_desc = 0;
struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev);
struct device *dev = &cdev->auxdev.dev;
struct sof_ipc4_probe_info *info;
struct sof_ipc4_msg msg;
u32 param_id;
int i, ret;
if (!mentry)
return -ENODEV;
switch (type) {
case PROBES_INFO_ACTIVE_PROBES:
param_id = SOF_IPC4_PROBE_POINTS;
break;
case PROBES_INFO_AVAILABE_PROBES:
param_id = SOF_IPC4_PROBE_POINTS_AVAILABLE;
break;
default:
dev_err(dev, "%s: info type %u not supported", __func__, type);
return -EOPNOTSUPP;
}
msg.primary = mentry->id;
msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(param_id);
msg.data_size = sof_client_get_ipc_max_payload_size(cdev);
msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL);
if (!msg.data_ptr)
return -ENOMEM;
ret = sof_client_ipc_set_get_data(cdev, &msg, false);
if (ret) {
kfree(msg.data_ptr);
return ret;
}
info = msg.data_ptr;
*num_desc = info->num_elems;
dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc);
*desc = kzalloc(*num_desc * sizeof(**desc), GFP_KERNEL);
if (!*desc) {
kfree(msg.data_ptr);
return -ENOMEM;
}
for (i = 0; i < *num_desc; i++) {
(*desc)[i].buffer_id = info->points[i].point_id;
(*desc)[i].purpose = info->points[i].purpose;
(*desc)[i].stream_tag = info->points[i].stream_tag;
}
kfree(msg.data_ptr);
return 0;
}
/**
* ipc4_probes_point_print - Human readable print of probe point descriptor
* @cdev: SOF client device
* @buf: Buffer to print to
* @size: Available bytes in buffer
* @desc: Describes the probe point to print
* @return: Number of bytes printed or an error code (snprintf return value)
*/
static int ipc4_probes_point_print(struct sof_client_dev *cdev, char *buf, size_t size,
struct sof_probe_point_desc *desc)
{
struct device *dev = &cdev->auxdev.dev;
struct snd_sof_widget *swidget;
int ret;
swidget = sof_client_ipc4_find_swidget_by_id(cdev, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
if (!swidget)
dev_err(dev, "%s: Failed to find widget for module %lu.%lu\n",
__func__, SOF_IPC4_MOD_ID_GET(desc->buffer_id),
SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id));
ret = snprintf(buf, size, "%#x,%#x,%#x\t%s %s buf idx %lu %s\n",
desc->buffer_id, desc->purpose, desc->stream_tag,
swidget ? swidget->widget->name : "<unknown>",
sof_probe_ipc4_type_string(SOF_IPC4_PROBE_TYPE_GET(desc->buffer_id)),
SOF_IPC4_PROBE_IDX_GET(desc->buffer_id),
desc->stream_tag ? "(connected)" : "");
return ret;
}
/**
* ipc4_probes_points_add - connect specified probes
* @cdev: SOF client device
@@ -202,7 +319,7 @@ static int ipc4_probes_points_add(struct sof_client_dev *cdev,
int i, ret;
if (!mentry)
return -ENODEV;
return -EOPNOTSUPP;
/* The sof_probe_point_desc and sof_ipc4_probe_point structs
* are of same size and even the integers are the same in the
@@ -286,6 +403,7 @@ const struct sof_probes_ipc_ops ipc4_probe_ops = {
.init = ipc4_probes_init,
.deinit = ipc4_probes_deinit,
.points_info = ipc4_probes_points_info,
.point_print = ipc4_probes_point_print,
.points_add = ipc4_probes_points_add,
.points_remove = ipc4_probes_points_remove,
};

View File

@@ -17,8 +17,14 @@
#include <sound/soc.h>
#include <sound/sof/header.h>
#include <sound/sof/ipc4/header.h>
#include "sof-client.h"
#include "sof-client-probes.h"
#include "sof-audio.h"
#ifdef CONFIG_SND_SOC_SOF_IPC4
#include "ipc4-priv.h"
#endif
#define SOF_PROBES_SUSPEND_DELAY_MS 3000
/* only extraction supported for now */
@@ -69,7 +75,8 @@ static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
int i, ret;
/* disconnect all probe points */
ret = ipc->points_info(cdev, &desc, &num_desc);
ret = ipc->points_info(cdev, &desc, &num_desc,
PROBES_INFO_ACTIVE_PROBES);
if (ret < 0) {
dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
goto exit;
@@ -189,7 +196,8 @@ static const struct snd_compress_ops sof_probes_compressed_ops = {
};
static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
size_t count, loff_t *ppos)
size_t count, loff_t *ppos,
enum sof_probe_info_type type)
{
struct sof_client_dev *cdev = file->private_data;
struct sof_probes_priv *priv = cdev->data;
@@ -216,16 +224,20 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
goto exit;
}
ret = ipc->points_info(cdev, &desc, &num_desc);
ret = ipc->points_info(cdev, &desc, &num_desc, type);
if (ret < 0)
goto pm_error;
for (i = 0; i < num_desc; i++) {
offset = strlen(buf);
remaining = PAGE_SIZE - offset;
ret = snprintf(buf + offset, remaining,
"Id: %#010x Purpose: %u Node id: %#x\n",
desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
if (ipc->point_print)
ret = ipc->point_print(cdev, buf + offset, remaining, &desc[i]);
else
ret = snprintf(buf + offset, remaining,
"Id: %#010x Purpose: %u Node id: %#x\n",
desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
if (ret < 0 || ret >= remaining) {
/* truncate the output buffer at the last full line */
buf[offset] = '\0';
@@ -247,6 +259,22 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
return ret;
}
static ssize_t sof_probes_dfs_active_points_read(struct file *file,
char __user *to,
size_t count, loff_t *ppos)
{
return sof_probes_dfs_points_read(file, to, count, ppos,
PROBES_INFO_ACTIVE_PROBES);
}
static ssize_t sof_probes_dfs_available_points_read(struct file *file,
char __user *to,
size_t count, loff_t *ppos)
{
return sof_probes_dfs_points_read(file, to, count, ppos,
PROBES_INFO_AVAILABE_PROBES);
}
static ssize_t
sof_probes_dfs_points_write(struct file *file, const char __user *from,
size_t count, loff_t *ppos)
@@ -296,15 +324,23 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
return ret;
}
static const struct file_operations sof_probes_points_fops = {
static const struct file_operations sof_probes_active_points_fops = {
.open = simple_open,
.read = sof_probes_dfs_points_read,
.read = sof_probes_dfs_active_points_read,
.write = sof_probes_dfs_points_write,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
static const struct file_operations sof_probes_available_points_fops = {
.open = simple_open,
.read = sof_probes_dfs_available_points_read,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
static ssize_t
sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
size_t count, loff_t *ppos)
@@ -449,13 +485,17 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev,
/* create read-write probes_points debugfs entry */
priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
cdev, &sof_probes_points_fops);
cdev, &sof_probes_active_points_fops);
/* create read-write probe_points_remove debugfs entry */
priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
dfsroot, cdev,
&sof_probes_points_remove_fops);
/* create read-write probes_points debugfs entry */
priv->dfs_points = debugfs_create_file("probe_points_available", 0644, dfsroot,
cdev, &sof_probes_available_points_fops);
links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
if (!links || !cpus) {

View File

@@ -34,13 +34,20 @@ struct sof_probe_point_desc {
unsigned int stream_tag;
} __packed;
enum sof_probe_info_type {
PROBES_INFO_ACTIVE_PROBES,
PROBES_INFO_AVAILABE_PROBES,
};
struct sof_probes_ipc_ops {
int (*init)(struct sof_client_dev *cdev, u32 stream_tag,
size_t buffer_size);
int (*deinit)(struct sof_client_dev *cdev);
int (*points_info)(struct sof_client_dev *cdev,
struct sof_probe_point_desc **desc,
size_t *num_desc);
size_t *num_desc, enum sof_probe_info_type type);
int (*point_print)(struct sof_client_dev *cdev, char *buf, size_t size,
struct sof_probe_point_desc *desc);
int (*points_add)(struct sof_client_dev *cdev,
struct sof_probe_point_desc *desc,
size_t num_desc);

View File

@@ -380,6 +380,19 @@ struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c,
return NULL;
}
EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, "SND_SOC_SOF_CLIENT");
struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev,
u32 module_id, int instance_id)
{
struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4)
return sof_ipc4_find_swidget_by_ids(sdev, module_id, instance_id);
dev_err(sdev->dev, "Only supported with IPC4\n");
return NULL;
}
EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_swidget_by_id, "SND_SOC_SOF_CLIENT");
#endif
int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)

View File

@@ -41,6 +41,8 @@ int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
bool set);
struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *u);
struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev,
u32 module_id, int instance_id);
struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);