diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b0b22e6ebc03ff..fc29fff7b568be 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@ snd-sof-y := 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 + fw-file-profile.o debug-dsp-ops.o # IPC implementations ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index 2d5e58846499d2..552d47c9813483 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -263,7 +263,7 @@ int acp_sof_dsp_run(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON); -int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) +int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev, const char *fw_name) { struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata = plat_data->hw_pdata; diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 87e79d500865a8..324718e4cd7d4b 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -270,7 +270,7 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev); /* DSP Loader callbacks */ int acp_sof_dsp_run(struct snd_sof_dev *sdev); int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev); -int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev); +int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev, const char *fw_filename); int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type); /* Block IO callbacks */ diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 83fe0401baf867..aa83f9fb400e78 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -418,8 +418,14 @@ static int sof_init_environment(struct snd_sof_dev *sdev) static int sof_probe_continue(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_dsp_power_state state = { + .state = SOF_DSP_PM_D3, + }; int ret; + /* set initial power state to D3 */ + sdev->dsp_power_state = state; + /* Initialize loadable file paths and check the environment validity */ ret = sof_init_environment(sdev); if (ret) @@ -456,8 +462,19 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto ipc_err; } + /* + * skip loading/booting firmware and registering the machine driver when DSP OPS testing + * is enabled with IPC4. Normal audio operations will be unavailable in this mode. + */ + if (sof_debug_check_flag(SOF_DBG_DSP_OPS_TEST_MODE) && + sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + sdev->dsp_test_mode_enabled = true; + sdev->probe_completed = true; + return 0; + } + /* 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); @@ -582,9 +599,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) /* initialize sof device */ sdev->dev = dev; - /* initialize default DSP power state */ - sdev->dsp_power_state.state = SOF_DSP_PM_D0; - sdev->pdata = plat_data; sdev->first_boot = true; dev_set_drvdata(dev, sdev); diff --git a/sound/soc/sof/debug-dsp-ops.c b/sound/soc/sof/debug-dsp-ops.c new file mode 100644 index 00000000000000..02215f124cb5ad --- /dev/null +++ b/sound/soc/sof/debug-dsp-ops.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2024 Intel Corporation. +// + +#include +#include +#include "sof-priv.h" +#include "ops.h" + +#define MAX_FW_STATE_STRING_LEN 128 + +/* + * set dsp power state op by writing the requested power state. + * ex: echo D3 > dsp_power_state + */ +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); + struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D3, + }; + int ret; + + /* 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_set_power_state(sdev, &target_state); + if (ret < 0) + return ret; + + sdev->enabled_cores_mask = 0; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); + } + + return 0; +} + +static int sof_dsp_ops_trace_init(struct snd_sof_dev *sdev, bool init) +{ + if (init) + return sof_fw_trace_init(sdev); + + sof_fw_trace_free(sdev); + return 0; +} + +static void sof_dsp_ops_unload_firmware(struct snd_sof_dev *sdev) +{ + /* release the currently loaded firmware */ + if (sdev->basefw.fw) + snd_sof_fw_unload(sdev); + + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); +} + +static int sof_dsp_ops_boot_firmware(struct snd_sof_dev *sdev) +{ + const char *fw_filename = NULL; + int ret; + + if (sdev->test_profile.fw_name && strlen(sdev->test_profile.fw_name)) { + if (sdev->test_profile.fw_path && strlen(sdev->test_profile.fw_path)) + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", sdev->test_profile.fw_path, + sdev->test_profile.fw_name); + else + fw_filename = kasprintf(GFP_KERNEL, "%s", sdev->test_profile.fw_name); + } + + /* If fw_filename is NULL the firmware from the default profile will be loaded */ + ret = snd_sof_load_firmware(sdev, fw_filename); + kfree(fw_filename); + if (ret < 0) + return ret; + + /* boot firmware */ + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); + + ret = snd_sof_run_firmware(sdev); + + /* set first_boot to false so subsequent boots will be from IMR if supported */ + if (!ret) + sdev->first_boot = false; + + return ret; +} + +static ssize_t sof_dsp_ops_fw_state_read(struct snd_sof_dev *sdev, char __user *buffer, + size_t count, loff_t *ppos) +{ + char string[MAX_FW_STATE_STRING_LEN]; + size_t size_ret; + + switch (sdev->fw_state) { + case SOF_FW_BOOT_NOT_STARTED: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: NOT STARTED\n", sdev->fw_state); + break; + case SOF_DSPLESS_MODE: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: DSPLESS MODE\n", sdev->fw_state); + break; + case SOF_FW_BOOT_PREPARE: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: PREPARE\n", sdev->fw_state); + break; + case SOF_FW_BOOT_IN_PROGRESS: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: BOOT IN PROGRESS\n", sdev->fw_state); + break; + case SOF_FW_BOOT_FAILED: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: FAILED\n", sdev->fw_state); + break; + case SOF_FW_BOOT_READY_FAILED: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: READY FAILED\n", sdev->fw_state); + break; + case SOF_FW_BOOT_READY_OK: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: READY OK\n", sdev->fw_state); + break; + case SOF_FW_BOOT_COMPLETE: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: COMPLETE\n", sdev->fw_state); + break; + case SOF_FW_CRASHED: + snprintf(string, MAX_FW_STATE_STRING_LEN, + "%d: CRASHED\n", sdev->fw_state); + break; + default: + break; + } + + if (*ppos) + return 0; + + count = min_t(size_t, count, strlen(string)); + size_ret = copy_to_user(buffer, string, count); + if (size_ret) + return -EFAULT; + + *ppos += count; + + return count; +} + +static ssize_t sof_dsp_ops_tester_dfs_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct dentry *dentry; + const char *string; + size_t size_ret; + + /* return the FW filename or path */ + dentry = file->f_path.dentry; + if (!strcmp(dentry->d_name.name, "fw_filename")) { + string = sdev->test_profile.fw_name; + } else if (!strcmp(dentry->d_name.name, "fw_path")) { + string = sdev->test_profile.fw_path; + } else if (!strcmp(dentry->d_name.name, "dsp_power_state")) { + switch (sdev->dsp_power_state.state) { + case SOF_DSP_PM_D0: + string = "D0\n"; + break; + case SOF_DSP_PM_D3: + string = "D3\n"; + break; + default: + break; + } + } else if (!strcmp(dentry->d_name.name, "fw_state")) { + return sof_dsp_ops_fw_state_read(sdev, buffer, count, ppos); + } else { + return 0; + } + + if (*ppos || !string) + return 0; + + count = min_t(size_t, count, strlen(string)); + size_ret = copy_to_user(buffer, string, count); + if (size_ret) + return -EFAULT; + + *ppos += count; + + return count; +} + +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 dentry *dentry = file->f_path.dentry; + struct snd_sof_dev *sdev = dfse->sdev; + size_t size; + char *string; + + + if (!strcmp(dentry->d_name.name, "init_trace")) + return sof_dsp_ops_trace_init(sdev, true); + + if (!strcmp(dentry->d_name.name, "free_trace")) + return sof_dsp_ops_trace_init(sdev, false); + + if (!strcmp(dentry->d_name.name, "unload_fw")) { + string = kzalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + size = simple_write_to_buffer(string, count, ppos, buffer, count); + kfree(string); + sof_dsp_ops_unload_firmware(sdev); + return size; + } + + if (!strcmp(dentry->d_name.name, "boot_fw")) { + int ret; + string = kzalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + size = simple_write_to_buffer(string, count, ppos, buffer, count); + kfree(string); + + ret = sof_dsp_ops_boot_firmware(sdev); + if (ret < 0) + return ret; + return size; + } + + /* set DSP power state */ + if (!strcmp(dentry->d_name.name, "dsp_power_state")) { + 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'; + ret = sof_dsp_ops_set_power_state(sdev, string); + kfree(string); + if (ret < 0) + return ret; + + return size; + } + + if (strcmp(dentry->d_name.name, "fw_filename") && + strcmp(dentry->d_name.name, "fw_path")) + return 0; + + string = devm_kzalloc(sdev->dev, 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'; + + if (!strcmp(dentry->d_name.name, "fw_filename")) { + if (sdev->test_profile.fw_name) + devm_kfree(sdev->dev, sdev->test_profile.fw_name); + sdev->test_profile.fw_name = string; + + return size; + } + + if (sdev->test_profile.fw_path) + devm_kfree(sdev->dev, sdev->test_profile.fw_path); + sdev->test_profile.fw_path = string; + + return size; +} + +static const struct file_operations sof_dsp_ops_tester_fops = { + .open = simple_open, + .write = sof_dsp_ops_tester_dfs_write, + .read = sof_dsp_ops_tester_dfs_read, +}; + +static int sof_dsp_dsp_ops_create_dfse(struct snd_sof_dev *sdev, const char *name, + struct dentry *parent, umode_t mode) +{ + struct snd_sof_dfsentry *dfse; + + /* create debugfs entry for FW filename */ + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_BUF; + dfse->sdev = sdev; + debugfs_create_file(name, mode, parent, dfse, &sof_dsp_ops_tester_fops); + list_add(&dfse->list, &sdev->dfsentry_list); + + return 0; +} + +int sof_dbg_dsp_ops_test_init(struct snd_sof_dev *sdev) +{ + struct dentry *dsp_ops_debugfs; + int ret; + + /* debugfs root directory for DSP ops debug */ + dsp_ops_debugfs = debugfs_create_dir("fw_debug_ops", sdev->debugfs_root); + + /* create debugfs entry for FW filename */ + ret = sof_dsp_dsp_ops_create_dfse(sdev, "fw_filename", dsp_ops_debugfs, 0666); + if (ret < 0) + return ret; + + /* create debugfs entry for FW path */ + ret = sof_dsp_dsp_ops_create_dfse(sdev, "fw_path", dsp_ops_debugfs, 0666); + if (ret < 0) + return ret; + + ret = sof_dsp_dsp_ops_create_dfse(sdev, "boot_fw", dsp_ops_debugfs, 0222); + if (ret < 0) + return ret; + + ret = sof_dsp_dsp_ops_create_dfse(sdev, "dsp_power_state", dsp_ops_debugfs, 0666); + if (ret < 0) + return ret; + + ret = sof_dsp_dsp_ops_create_dfse(sdev, "fw_state", dsp_ops_debugfs, 0444); + if (ret < 0) + return ret; + + ret = sof_dsp_dsp_ops_create_dfse(sdev, "unload_fw", dsp_ops_debugfs, 0222); + if (ret < 0) + return ret; + + ret = sof_dsp_dsp_ops_create_dfse(sdev, "init_trace", dsp_ops_debugfs, 0222); + if (ret < 0) + return ret; + + return sof_dsp_dsp_ops_create_dfse(sdev, "free_trace", dsp_ops_debugfs, 0222); +} diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d0ffa1d7114504..ba99b777362f5e 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -371,9 +371,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), - "fw_state", 0444); + 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); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 4c88522d404847..c7a54698fa638f 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -684,8 +684,22 @@ static int hda_dsp_set_power_state(struct snd_sof_dev *sdev, case SOF_DSP_PM_D3: /* The only allowed transition is: D0I0 -> D3 */ if (sdev->dsp_power_state.state == SOF_DSP_PM_D0 && - sdev->dsp_power_state.substate == SOF_HDA_DSP_PM_D0I0) + sdev->dsp_power_state.substate == SOF_HDA_DSP_PM_D0I0) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + int ret, j; + + ret = chip->power_down_dsp(sdev); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to power down DSP\n", __func__); + return ret; + } + + /* reset ref counts for all cores */ + for (j = 0; j < chip->cores_num; j++) + sdev->dsp_core_ref_count[j] = 0; break; + } dev_err(sdev->dev, "error: transition from %d to %d not allowed\n", @@ -781,8 +795,11 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D3, + }; bool imr_lost = false; - int ret, j; + int ret; /* * The memory used for IMR boot loses its content in deeper than S3 @@ -820,15 +837,9 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) if (sdev->dspless_mode_selected) goto skip_dsp; - ret = chip->power_down_dsp(sdev); - if (ret < 0) { - dev_err(sdev->dev, "failed to power down DSP during suspend\n"); + ret = snd_sof_dsp_set_power_state(sdev, &target_state); + if (ret < 0) return ret; - } - - /* reset ref counts for all cores */ - for (j = 0; j < chip->cores_num; j++) - sdev->dsp_core_ref_count[j] = 0; /* disable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, false); @@ -988,10 +999,6 @@ EXPORT_SYMBOL_NS(hda_dsp_runtime_idle, SND_SOC_SOF_INTEL_HDA_COMMON); int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - const struct sof_dsp_power_state target_state = { - .state = SOF_DSP_PM_D3, - }; - int ret; if (!sdev->dspless_mode_selected) { /* cancel any attempt for DSP D0I3 */ @@ -999,11 +1006,7 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev) } /* stop hda controller and power dsp off */ - ret = hda_suspend(sdev, true); - if (ret < 0) - return ret; - - return snd_sof_dsp_set_power_state(sdev, &target_state); + return hda_suspend(sdev, true); } EXPORT_SYMBOL_NS(hda_dsp_runtime_suspend, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -1059,12 +1062,10 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) /* stop hda controller and power dsp off */ ret = hda_suspend(sdev, false); - if (ret < 0) { - dev_err(bus->dev, "error: suspending dsp\n"); - return ret; - } + if (ret < 0) + dev_err(bus->dev, "failed to suspend dsp\n"); - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); + return ret; } EXPORT_SYMBOL_NS(hda_dsp_suspend, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 2b9d22ccf345b8..e767de085f04e8 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -306,12 +306,6 @@ int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) if (sdev->first_boot) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; - ret = hda_sdw_startup(sdev); - if (ret < 0) { - dev_err(sdev->dev, "could not startup SoundWire links\n"); - return ret; - } - /* Check if IMR boot is usable */ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) { hdev->imrboot_supported = true; @@ -319,6 +313,15 @@ int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) 0644, sdev->debugfs_root, &hdev->skip_imr_boot); } + + if (sdev->dsp_test_mode_enabled) + return 0; + + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "could not startup SoundWire links\n"); + return ret; + } } hda_sdw_int_enable(sdev, true); diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index bcdb33d0368209..456b9885d06ca1 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -149,13 +149,22 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) ssize_t payload_offset; int ret; - fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); + if (sdev->dsp_test_mode_enabled) + fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); + else + fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); if (!fw_lib) return -ENOMEM; fw_lib->sof_fw.fw = sdev->basefw.fw; payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib); + if (sdev->dsp_test_mode_enabled) { + devm_kfree(sdev->dev, fw_lib->modules); + devm_kfree(sdev->dev, fw_lib); + return payload_offset; + } + if (payload_offset > 0) { fw_lib->sof_fw.payload_offset = payload_offset; diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 0baf316b0064b6..d24cca5b877013 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -14,22 +14,25 @@ #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) { struct snd_sof_pdata *plat_data = sdev->pdata; - const char *fw_filename; ssize_t ext_man_size; + bool alloc = false; 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; + alloc = true; + } ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); @@ -59,17 +62,18 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) } err: - kfree(fw_filename); + if (alloc) + kfree(fw_filename); return ret; } 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; @@ -108,6 +112,9 @@ EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); int snd_sof_run_firmware(struct snd_sof_dev *sdev) { + struct sof_dsp_power_state state = { + .state = SOF_DSP_PM_D0, + }; int ret; init_waitqueue_head(&sdev->boot_wait); @@ -167,6 +174,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "firmware boot complete\n"); sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + /* set the DSP power state to D0 after successful firmware boot */ + sdev->dsp_power_state = state; + /* perform post fw run operations */ ret = snd_sof_dsp_post_fw_run(sdev); if (ret < 0) { diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 2584621c3b2d48..6961eba3a1061b 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -474,12 +474,14 @@ snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, return 0; } -/* Firmware loading */ -static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev) +/* Firmware loading. If the fw_filename argument is NULL, the firmware file set in the + * default profile will be loaded. Otherwise, it must contain the full path to the file. + */ +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 */ diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8e3bcf602beb31..7f3c42662e724e 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -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", diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4d6a1517f9b351..a41567711bb8db 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -52,6 +52,10 @@ struct snd_sof_pcm_stream; * dump the message payload also */ #define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */ +#define SOF_DBG_DSP_OPS_TEST_MODE BIT(16) /* Used for DSP ops testing when + * DSPLESS_MODE is not set. + * No audio functionality when enabled. + */ /* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) @@ -228,7 +232,7 @@ struct snd_sof_dsp_ops { struct snd_sof_ipc_msg *msg); /* mandatory */ /* FW loading */ - int (*load_firmware)(struct snd_sof_dev *sof_dev); /* mandatory */ + int (*load_firmware)(struct snd_sof_dev *sof_dev, const char *fw_filename); /* mandatory */ int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); /* optional */ @@ -565,6 +569,13 @@ struct snd_sof_dev { */ bool dspless_mode_selected; + /* + * Flag set when DSP ops testing is enabled. When this is set normal audio functionality + * will be disabled. Note that the DSP ops testing cannot be enabled when dspless mode is + * selected. + */ + bool dsp_test_mode_enabled; + /* Main, Base firmware image */ struct sof_firmware basefw; @@ -689,6 +700,9 @@ struct snd_sof_dev { bool mclk_id_override; u16 mclk_id_quirk; /* same size as in IPC3 definitions */ + /* used to store the FW filename and path passed from userspace for DSP ops testing */ + struct sof_loadable_file_profile test_profile; + void *private; /* core does not touch this */ }; @@ -727,8 +741,8 @@ int sof_create_ipc_file_profile(struct snd_sof_dev *sdev, /* * Firmware loading. */ -int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev); -int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev); +int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev, const char *fw_filename); +int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev, const char *fw_filename); int snd_sof_run_firmware(struct snd_sof_dev *sdev); void snd_sof_fw_unload(struct snd_sof_dev *sdev); @@ -898,4 +912,6 @@ static inline int sof_resume_clients(struct snd_sof_dev *sdev) extern const struct sof_ipc_ops ipc3_ops; extern const struct sof_ipc_ops ipc4_ops; +int sof_dbg_dsp_ops_test_init(struct snd_sof_dev *sdev); + #endif