mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 11:06:41 -05:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user