Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions sound/soc/sof/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT
Say Y if you want to retain DSP context for FW exceptions.
If unsure, select "N".

config SND_SOC_SOF_DEBUG_DSP_OPS_TEST
bool "SOF enable DSP ops testing"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does constitute a "DSP ops"? Isn't this something like "DSP test mode"? Should we have a sof_debug flag for this to be enabled/disabled instead of re-building the kernel?

help
This option is used for testing DSP operations such as load/unload FW,
set power state, enable/disable core etc. from userspace.
Say Y if you want to retain DSP context for FW exceptions.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that line looks like a copy-paste from above?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would mention that this will disable the audio support completely

If unsure, select "N".

endif ## SND_SOC_SOF_DEBUG

endif ## SND_SOC_SOF_DEVELOPER_SUPPORT
Expand Down
3 changes: 3 additions & 0 deletions sound/soc/sof/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
fw-file-profile.o

ifneq ($(CONFIG_SND_SOC_SOF_DEBUG_DSP_OPS_TEST),)
snd-sof-objs += debug-dsp-ops.o
endif
# IPC implementations
ifneq ($(CONFIG_SND_SOC_SOF_IPC3),)
snd-sof-objs += ipc3.o ipc3-loader.o ipc3-topology.o ipc3-control.o ipc3-pcm.o\
Expand Down
10 changes: 8 additions & 2 deletions sound/soc/sof/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
}

/* load the firmware */
ret = snd_sof_load_firmware(sdev);
ret = snd_sof_load_firmware(sdev, NULL);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
ret);
Expand Down Expand Up @@ -472,6 +472,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
/* hereafter all FW boot flows are for PM reasons */
sdev->first_boot = false;

#if !IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DSP_OPS_TEST)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use if (!IS_ENABLED) instead of #if

/* now register audio DSP platform driver and dai */
ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
sof_ops(sdev)->drv,
Expand All @@ -488,6 +489,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
"error: failed to register machine driver %d\n", ret);
goto fw_trace_err;
}
#endif

ret = sof_register_clients(sdev);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we loading clients in DSP test mode? They might interfere with the testing, no?

if (ret < 0) {
Expand All @@ -511,8 +513,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return 0;

sof_machine_err:
#if !IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DSP_OPS_TEST)
snd_sof_machine_unregister(sdev, plat_data);

fw_trace_err:
#endif
sof_fw_trace_free(sdev);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also enable ftrace? That will send messages, again, might interfere with testing?

fw_run_err:
snd_sof_fw_unload(sdev);
Expand Down Expand Up @@ -567,6 +572,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
if (sof_core_debug)
dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);

#if !IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DSP_OPS_TEST)
if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) {
if (plat_data->desc->dspless_mode_supported) {
dev_info(dev, "Switching to DSPless mode\n");
Expand All @@ -575,7 +581,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
dev_info(dev, "DSPless mode is not supported by the platform\n");
}
}

#endif
/* Initialize sof_ops based on the initial selected IPC version */
ret = sof_init_sof_ops(sdev);
if (ret)
Expand Down
161 changes: 161 additions & 0 deletions sound/soc/sof/debug-dsp-ops.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2023 Intel Corporation. All rights reserved.
//

#include <linux/debugfs.h>
#include <sound/sof/debug.h>
#include "sof-priv.h"
#include "ops.h"

/*
* set dsp power state op by writing the power state.
* ex: echo set_power_state,D3 > dsp_test_op
*/
static int sof_dsp_ops_set_power_state(struct snd_sof_dev *sdev, char *state)
{
/* only D3 supported for now */
if (strcmp(state, "D3")) {
dev_err(sdev->dev, "Unsupported state %s\n", state);
return -EINVAL;
}

/* power off the DSP */
if (sdev->dsp_power_state.state == SOF_DSP_PM_D0) {
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
pm_message_t pm_state;
int ret;

pm_state.event = SOF_DSP_PM_D3;

/* suspend DMA trace */
sof_fw_trace_suspend(sdev, pm_state);

/* notify DSP of upcoming power down */
if (pm_ops && pm_ops->ctx_save) {
ret = pm_ops->ctx_save(sdev);
if (ret < 0)
return ret;
}

ret = snd_sof_dsp_runtime_suspend(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to power off DSP\n");
return ret;
}

sdev->enabled_cores_mask = 0;
sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
}

return 0;
}

/*
* test firmware boot by passing the firmware file as the argument:
* ex: echo boot_firmware,intel/avs/tgl/community/dsp_basefw.bin > dsp_test_op
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why combine the two parts?

echo intel/avs/tgl/community/dsp_basefw.bin > filename
echo 1 > boot

*/
static int sof_dsp_ops_boot_firmware(struct snd_sof_dev *sdev, char *fw_filename)
{
int ret;

/* power off the DSP */
ret = sof_dsp_ops_set_power_state(sdev, "D3");
if (ret < 0)
return ret;

if (sdev->basefw.fw)
snd_sof_fw_unload(sdev);

ret = snd_sof_dsp_runtime_resume(sdev);
if (ret < 0)
return ret;

sdev->first_boot = true;

/* load and boot firmware */
ret = snd_sof_load_firmware(sdev, (const char *)fw_filename);
if (ret < 0)
return ret;

sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);

ret = snd_sof_run_firmware(sdev);
if (ret < 0)
return ret;

/* resume DMA trace */
return sof_fw_trace_resume(sdev);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like code duplication, I don't follow what it's supposed to do

Do we really want to do this or run something like the python stuff on top of a simplified SOF driver.

We have to be really careful not to open a pandora box of new stuff that we'll have to maintain forever twice.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@plbossart the idea is to be able to run the python scripts on Linux with minimal effort by using the debugfs option. This is in essence a simplified SOF driver isnt it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this basically an alternative to https://github.com/thesofproject/sof-diagnostic-driver that don't have to load/unload?

Isn't the sof-diagnostic-driver approach more generic/flexible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the question is really "what is the partitioning between scripts and driver"? Do we want to reuse snd_sof_run_firmware and hard-code it in a sequence used by a script, or do we want the scripts to do something that runs the firmware?
If you really want to use scripts, then the driver part should be rather minimal and probably a pass-through to access registers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but @plbossart if we want to write scripts to do all the register reads/writes, it would essentially be rewriting the SOF driver. Isn't this a saner approach?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but @plbossart if we want to write scripts to do all the register reads/writes, it would essentially be rewriting the SOF driver. Isn't this a saner approach?

the question is whether
a) you want to reuse the existing Python scripts used by FW validation or
b) you want to develop NEW python scripts based on a TBD API

It's not clear from this PR what the direction is.
a) is complicated because I don't know what the interface requirements are, but
b) doesn't seem very appealing to me since it'd be a 3rd way of interacting with the DSP.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marc-hb this isnt a replacement for the diagnostic driver. This is a way to enable us to run the python scripts for FW testing on Linux with minimal effort.

What other purpose does the sof-diagnostic-driver have?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ranj063, it depends which python script we are talking about... One set I have seen is implementing IPC message crafting, sending (yep, with register poking and then polling for completion).
It might be hard to get it to download the firmware because of the DMA configuring, that is true.

Copy link
Member

@lgirdwood lgirdwood Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@plbossart @ujfalusi @andyross @marc-hb this PR is NOT about IPC ABI validation but a feature to support debug and compliance/stress testing of kernel + SOF + Zephyr functional IP programming flows with userspace scripting (not userspace IP programming).

We need to test the kernel functional IP code alongside the associated Zephyr IP code together (with the same timing and sequencing) which is not possible using Python to flip bits via mmap(). We already have a Python mmap() CI tool today that verifies IPC ABI compliance only. i.e. not useful to certify IP programming compliance.

Yes we can do a lot of test with aplay/amixer, but these tools are end user applications not designed to strongly validate/stress the kernel + SOF + Zephyr combination.

e.g. we need to be able to stress (yes, low hanging fruit will come first)

  1. D3 -> D0 and vice versa
  2. DSP core off/on
  3. S0ix entry/exit
  4. FW boot
  5. IMR save/restore
  6. topology load/unload

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will assert that you can do all of the above with sof-test. Just run thousands of cycles of suspend-resume, module load/unload, etc.

}

/* ops are executed as "op_name,argument1,argument2...". For example, to set the DSP power state
* to D3: echo "load_firmware,<PATH>/sof-tgl.ri" > dsp_test_op"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not separate files for the firmware path, set_power_state, etc?
How one would guess the valid op_names and what they are set at the moment?

*/
static ssize_t sof_dsp_ops_tester_dfs_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct snd_sof_dfsentry *dfse = file->private_data;
struct snd_sof_dev *sdev = dfse->sdev;
size_t size;
const char *op_name;
char *string;
int ret;

string = kzalloc(count + 1, GFP_KERNEL);
if (!string)
return -ENOMEM;

size = simple_write_to_buffer(string, count, ppos, buffer, count);

/* truncate the \n at the end */
string[count - 1] = '\0';

/* extract the name of the op to execute */
op_name = strsep(&string, ",");
if (!op_name)
op_name = (const char *)string;

if (!strcmp(op_name, "boot_firmware")) {
ret = sof_dsp_ops_boot_firmware(sdev, string);
if (ret < 0)
goto err;
}

if (!strcmp(op_name, "set_power_state")) {
ret = sof_dsp_ops_set_power_state(sdev, string);
if (ret < 0)
goto err;
}

err:
if (ret >= 0)
ret = size;

kfree(string);

return ret;
}

static const struct file_operations sof_dsp_ops_tester_fops = {
.open = simple_open,
.write = sof_dsp_ops_tester_dfs_write,
};

int sof_dbg_dsp_ops_test_init(struct snd_sof_dev *sdev)
{
struct snd_sof_dfsentry *dfse;

dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
if (!dfse)
return -ENOMEM;

/* no need to allocate dfse buffer */
dfse->type = SOF_DFSENTRY_TYPE_BUF;
dfse->sdev = sdev;

debugfs_create_file("dsp_test_op", 0222, sdev->debugfs_root, dfse, &sof_dsp_ops_tester_fops);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dsp_test or rather separate files under dsp_test/ directory?


/* add to dfsentry list */
list_add(&dfse->list, &sdev->dfsentry_list);
return 0;
}
7 changes: 5 additions & 2 deletions sound/soc/sof/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,12 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
return err;
}

return snd_sof_debugfs_buf_item(sdev, &sdev->fw_state,
sizeof(sdev->fw_state),
err = snd_sof_debugfs_buf_item(sdev, &sdev->fw_state, sizeof(sdev->fw_state),
"fw_state", 0444);
if (err < 0)
return err;

return sof_dbg_dsp_ops_test_init(sdev);
}
EXPORT_SYMBOL_GPL(snd_sof_dbg_init);

Expand Down
8 changes: 5 additions & 3 deletions sound/soc/sof/ipc4-loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,8 @@ static ssize_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev,

static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_fw_library *fw_lib;
ssize_t payload_offset;
int ret;

fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL);
if (!fw_lib)
Expand All @@ -148,7 +146,11 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev)
fw_lib->sof_fw.fw = sdev->basefw.fw;

payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib);
#if !IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DSP_OPS_TEST)
if (payload_offset > 0) {
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret;

fw_lib->sof_fw.payload_offset = payload_offset;

/* basefw ID is 0 */
Expand All @@ -157,7 +159,7 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev)
if (ret)
return ret;
}

#endif
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we skipping this part?

return payload_offset;
}

Expand Down
19 changes: 10 additions & 9 deletions sound/soc/sof/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@
#include "sof-priv.h"
#include "ops.h"

int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev, const char *fw_filename)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should not neglect the snd_sof_load_firmware_memcpy()

{
struct snd_sof_pdata *plat_data = sdev->pdata;
const char *fw_filename;
ssize_t ext_man_size;
int ret;

/* Don't request firmware again if firmware is already requested */
if (sdev->basefw.fw)
return 0;

fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
plat_data->fw_filename_prefix,
plat_data->fw_filename);
if (!fw_filename)
return -ENOMEM;
if (!fw_filename) {
fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
plat_data->fw_filename_prefix,
plat_data->fw_filename);
if (!fw_filename)
return -ENOMEM;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, freeing up the passed fw_filename is OK?


ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev);

Expand Down Expand Up @@ -65,11 +66,11 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL(snd_sof_load_firmware_raw);

int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, const char *fw_filename)
{
int ret;

ret = snd_sof_load_firmware_raw(sdev);
ret = snd_sof_load_firmware_raw(sdev, fw_filename);
if (ret < 0)
return ret;

Expand Down
4 changes: 2 additions & 2 deletions sound/soc/sof/ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,11 @@ snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev,
}

/* Firmware loading */
static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev)
static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev, const char *fw_filename)
{
dev_dbg(sdev->dev, "loading firmware\n");

return sof_ops(sdev)->load_firmware(sdev);
return sof_ops(sdev)->load_firmware(sdev, fw_filename);
}

/* host DSP message data */
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/sof/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);

/* load the firmware */
ret = snd_sof_load_firmware(sdev);
ret = snd_sof_load_firmware(sdev, NULL);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to load DSP firmware after resume %d\n",
Expand Down
4 changes: 4 additions & 0 deletions sound/soc/sof/sof-pci-dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ static void sof_pci_probe_complete(struct device *dev)
if (sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME)
return;

#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_DSP_OPS_TEST)
return;
#endif

/* allow runtime_pm */
pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
Expand Down
Loading