// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) // // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // // Copyright(c) 2018 Intel Corporation // // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> //
/* Module parameters for firmware, topology and IPC type override */ staticchar *override_fw_path;
module_param_named(fw_path, override_fw_path, charp, 0444);
MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware.");
staticchar *override_fw_filename;
module_param_named(fw_filename, override_fw_filename, charp, 0444);
MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware.");
staticchar *override_lib_path;
module_param_named(lib_path, override_lib_path, charp, 0444);
MODULE_PARM_DESC(lib_path, "alternate path for SOF firmware libraries.");
staticchar *override_tplg_path;
module_param_named(tplg_path, override_tplg_path, charp, 0444);
MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
staticchar *override_tplg_filename;
module_param_named(tplg_filename, override_tplg_filename, charp, 0444);
MODULE_PARM_DESC(tplg_filename, "alternate filename for SOF topology.");
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG) staticunsignedint sof_ipc_timeout_ms; staticunsignedint sof_boot_timeout_ms;
module_param_named(ipc_timeout, sof_ipc_timeout_ms, uint, 0444);
MODULE_PARM_DESC(ipc_timeout, "Set the IPC timeout value in ms (0 to use the platform default)");
module_param_named(boot_timeout, sof_boot_timeout_ms, uint, 0444);
MODULE_PARM_DESC(boot_timeout, "Set the DSP boot timeout value in ms (0 to use the platform default)"); #endif
/* SOF defaults if not provided by the platform in ms */ #define TIMEOUT_DEFAULT_IPC_MS 500 #define TIMEOUT_DEFAULT_BOOT_MS 2000
/** * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug * @mask: Flag or combination of flags to check * * Returns true if all bits set in mask is also set in sof_core_debug, otherwise * false
*/ bool sof_debug_check_flag(int mask)
{ if ((sof_core_debug & mask) == mask) returntrue;
/** * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace * @sdev: Pointer to the device's sdev * @level: prink log level to use for the printing * @panic_code: the panic code * @tracep_code: tracepoint code * @oops: Pointer to DSP specific oops data * @panic_info: Pointer to the received panic information message * @stack: Pointer to the call stack data * @stack_words: Number of words in the stack data * * helper to be called from .dbg_dump callbacks. No error code is * provided, it's left as an exercise for the caller of .dbg_dump * (typically IPC or loader)
*/ void sof_print_oops_and_stack(struct snd_sof_dev *sdev, constchar *level,
u32 panic_code, u32 tracep_code, void *oops, struct sof_ipc_panic_info *panic_info, void *stack, size_t stack_words)
{
u32 code; int i;
/* is firmware dead ? */ if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n",
panic_code, tracep_code); return; /* no fault ? */
}
/* check IPC support */ if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) {
dev_err(sdev->dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
base_profile->ipc_type, plat_data->desc->ipc_supported_mask); return -EINVAL;
}
/* * Save the selected IPC type and a topology name override before * selecting ops since platform code might need this information
*/
plat_data->ipc_type = base_profile->ipc_type;
plat_data->tplg_filename = base_profile->tplg_name;
/* probe the DSP hardware */
ret = snd_sof_probe(sdev); if (ret < 0) {
dev_err(sdev->dev, "failed to probe DSP %d\n", ret); goto err_sof_probe;
}
/* check machine info */
ret = sof_machine_check(sdev); if (ret < 0) {
dev_err(sdev->dev, "failed to get machine info %d\n", ret); goto err_machine_check;
}
ret = sof_select_ipc_and_paths(sdev); if (ret) { goto err_machine_check;
} elseif (plat_data->ipc_type != base_profile->ipc_type) { /* IPC type changed, re-initialize the ops */
sof_ops_free(sdev);
ret = validate_sof_ops(sdev); if (ret < 0) {
snd_sof_remove(sdev);
snd_sof_remove_late(sdev); return ret;
}
}
/* Initialize loadable file paths and check the environment validity */
ret = sof_init_environment(sdev); if (ret) return ret;
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* set up platform component driver */
snd_sof_new_platform_drv(sdev);
if (sdev->dspless_mode_selected) {
sof_set_fw_state(sdev, SOF_DSPLESS_MODE); goto skip_dsp_init;
}
/* register any debug/trace capabilities */
ret = snd_sof_dbg_init(sdev); if (ret < 0) { /* * debugfs issues are suppressed in snd_sof_dbg_init() since * we cannot rely on debugfs * here we trap errors due to memory allocation only.
*/
dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n",
ret); goto dbg_err;
}
/* init the IPC */
sdev->ipc = snd_sof_ipc_init(sdev); if (!sdev->ipc) {
ret = -ENOMEM;
dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret); goto ipc_err;
}
/* load the firmware */
ret = snd_sof_load_firmware(sdev); if (ret < 0) {
dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_load_err;
}
sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
/* * Boot the firmware. The FW boot status will be modified * in snd_sof_run_firmware() depending on the outcome.
*/
ret = snd_sof_run_firmware(sdev); if (ret < 0) {
dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_run_err;
}
if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) {
sdev->fw_trace_is_supported = true;
/* init firmware tracing */
ret = sof_fw_trace_init(sdev); if (ret < 0) { /* non fatal */
dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n",
ret);
}
} else {
dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
}
skip_dsp_init: /* hereafter all FW boot flows are for PM reasons */
sdev->first_boot = false;
/* now register audio DSP platform driver and dai */
ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
sof_ops(sdev)->drv,
sof_ops(sdev)->num_drv); if (ret < 0) {
dev_err(sdev->dev, "error: failed to register DSP DAI driver %d\n", ret); goto fw_trace_err;
}
ret = snd_sof_machine_register(sdev, plat_data); if (ret < 0) {
dev_err(sdev->dev, "error: failed to register machine driver %d\n", ret); goto fw_trace_err;
}
ret = sof_register_clients(sdev); if (ret < 0) {
dev_err(sdev->dev, "failed to register clients %d\n", ret); goto sof_machine_err;
}
/* * Some platforms in SOF, ex: BYT, may not have their platform PM * callbacks set. Increment the usage count so as to * prevent the device from entering runtime suspend.
*/ if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
pm_runtime_get_noresume(sdev->dev);
if (plat_data->sof_probe_complete)
plat_data->sof_probe_complete(sdev->dev);
ret = sof_probe_continue(sdev); if (ret < 0) { /* errors cannot be propagated, log */
dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
}
}
staticvoid
sof_apply_profile_override(struct sof_loadable_file_profile *path_override, struct snd_sof_pdata *plat_data)
{ if (override_ipc_type >= 0 && override_ipc_type < SOF_IPC_TYPE_COUNT)
path_override->ipc_type = override_ipc_type; if (override_fw_path)
path_override->fw_path = override_fw_path; if (override_fw_filename)
path_override->fw_name = override_fw_filename; if (override_lib_path)
path_override->fw_lib_path = override_lib_path; if (override_tplg_path)
path_override->tplg_path = override_tplg_path; if (override_tplg_filename) {
path_override->tplg_name = override_tplg_filename; /* User requested a specific topology file and expect it to be loaded */
plat_data->disable_function_topology = true;
}
}
int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
{ struct snd_sof_dev *sdev; int ret;
sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); if (!sdev) return -ENOMEM;
/* initialize sof device */
sdev->dev = dev;
/* initialize default DSP power state */
sdev->dsp_power_state.state = SOF_DSP_PM_D0;
if (sof_core_debug)
dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);
if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) { if (plat_data->desc->dspless_mode_supported) {
dev_info(dev, "Switching to DSPless mode\n");
sdev->dspless_mode_selected = true;
} else {
dev_info(dev, "DSPless mode is not supported by the platform\n");
}
}
/* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0)
sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; else
sdev->ipc_timeout = plat_data->desc->ipc_timeout; if (plat_data->desc->boot_timeout == 0)
sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS; else
sdev->boot_timeout = plat_data->desc->boot_timeout;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG) /* Override the timeout values with module parameter, if set */ if (sof_ipc_timeout_ms)
sdev->ipc_timeout = sof_ipc_timeout_ms;
if (sof_boot_timeout_ms)
sdev->boot_timeout = sof_boot_timeout_ms; #endif
sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
/* * first pass of probe which isn't allowed to run in a work-queue, * typically to rely on -EPROBE_DEFER dependencies
*/
ret = snd_sof_probe_early(sdev); if (ret < 0) return ret;
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
INIT_WORK(&sdev->probe_work, sof_probe_work);
schedule_work(&sdev->probe_work); return 0;
}
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
aborted = cancel_work_sync(&sdev->probe_work);
/* * Unregister any registered client device first before IPC and debugfs * to allow client drivers to be removed cleanly
*/
sof_unregister_clients(sdev);
/* * Unregister machine driver. This will unbind the snd_card which * will remove the component driver and unload the topology * before freeing the snd_card.
*/
snd_sof_machine_unregister(sdev, pdata);
/* * Balance the runtime pm usage count in case we are faced with an * exception and we forcably prevented D3 power state to preserve * context
*/ if (sdev->d3_prevented) {
sdev->d3_prevented = false;
pm_runtime_put_noidle(sdev->dev);
}
if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
sof_fw_trace_free(sdev);
ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0)
dev_warn(dev, "error: %d failed to prepare DSP for device removal",
ret);
snd_sof_ipc_free(sdev);
snd_sof_free_debug(sdev);
snd_sof_remove(sdev);
snd_sof_remove_late(sdev);
sof_ops_free(sdev);
} elseif (aborted) { /* probe_work never ran */
snd_sof_remove_late(sdev);
sof_ops_free(sdev);
}
/* release firmware */
snd_sof_fw_unload(sdev);
return 0;
}
EXPORT_SYMBOL(snd_sof_device_remove);
int snd_sof_device_shutdown(struct device *dev)
{ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
sof_fw_trace_free(sdev); return snd_sof_shutdown(sdev);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.