// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
//
+#include <linux/module.h>
#include "ops.h"
#include "sof-priv.h"
#include "sof-audio.h"
+static int override_on_demand_boot = -1;
+module_param_named(on_demand_boot, override_on_demand_boot, int, 0444);
+MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - enabled");
+
/*
* Helper function to determine the target DSP state during
* system suspend. This function only cares about the device
}
#endif
-static int sof_resume(struct device *dev, bool runtime_resume)
+int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- u32 old_state = sdev->dsp_power_state.state;
int ret;
- /* do nothing if dsp resume callbacks are not set */
- if (!runtime_resume && !sof_ops(sdev)->resume)
- return 0;
-
- if (runtime_resume && !sof_ops(sdev)->runtime_resume)
- return 0;
-
- /* DSP was never successfully started, nothing to resume */
- if (sdev->first_boot)
- return 0;
-
- /*
- * if the runtime_resume flag is set, call the runtime_resume routine
- * or else call the system resume routine
- */
- if (runtime_resume)
- ret = snd_sof_dsp_runtime_resume(sdev);
- else
- ret = snd_sof_dsp_resume(sdev);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to power up DSP after resume\n");
- return ret;
- }
+ guard(mutex)(&sdev->dsp_fw_boot_mutex);
- if (sdev->dspless_mode_selected) {
- sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
+ /* Firmware already booted, just return */
return 0;
}
- /*
- * Nothing further to be done for platforms that support the low power
- * D0 substate. Resume trace and return when resuming from
- * low-power D0 substate
- */
- if (!runtime_resume && sof_ops(sdev)->set_power_state &&
- old_state == SOF_DSP_PM_D0) {
- ret = sof_fw_trace_resume(sdev);
- if (ret < 0)
- /* non fatal */
- dev_warn(sdev->dev,
- "failed to enable trace after resume %d\n", ret);
- return 0;
- }
+ dev_dbg(sdev->dev, "Booting DSP firmware\n");
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* load the firmware */
ret = snd_sof_load_firmware(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to load DSP firmware after resume %d\n",
- ret);
+ dev_err(sdev->dev, "%s: failed to load DSP firmware: %d\n",
+ __func__, ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
*/
ret = snd_sof_run_firmware(sdev);
if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to boot DSP firmware after resume %d\n",
- ret);
+ dev_err(sdev->dev, "%s: failed to boot DSP firmware: %d\n",
+ __func__, ret);
sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
return ret;
}
ret = sof_fw_trace_resume(sdev);
if (ret < 0) {
/* non fatal */
- dev_warn(sdev->dev,
- "warning: failed to init trace after resume %d\n",
- ret);
+ dev_warn(sdev->dev, "%s: failed to resume trace: %d\n",
+ __func__, ret);
}
/* restore pipelines */
if (tplg_ops && tplg_ops->set_up_all_pipelines) {
ret = tplg_ops->set_up_all_pipelines(sdev, false);
if (ret < 0) {
- dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
+ dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n",
+ __func__, ret);
goto setup_fail;
}
}
if (pm_ops && pm_ops->ctx_restore) {
ret = pm_ops->ctx_restore(sdev);
if (ret < 0)
- dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
+ dev_err(sdev->dev, "%s: ctx_restore IPC failed: %d\n",
+ __func__, ret);
}
setup_fail:
return ret;
}
+EXPORT_SYMBOL(snd_sof_boot_dsp_firmware);
+
+static int sof_resume(struct device *dev, bool runtime_resume)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ u32 old_state = sdev->dsp_power_state.state;
+ bool on_demand_boot;
+ int ret;
+
+ /* do nothing if dsp resume callbacks are not set */
+ if (!runtime_resume && !sof_ops(sdev)->resume)
+ return 0;
+
+ if (runtime_resume && !sof_ops(sdev)->runtime_resume)
+ return 0;
+
+ /* DSP was never successfully started, nothing to resume */
+ if (sdev->first_boot)
+ return 0;
+
+ /*
+ * if the runtime_resume flag is set, call the runtime_resume routine
+ * or else call the system resume routine
+ */
+ if (runtime_resume)
+ ret = snd_sof_dsp_runtime_resume(sdev);
+ else
+ ret = snd_sof_dsp_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "error: failed to power up DSP after resume\n");
+ return ret;
+ }
+
+ if (sdev->dspless_mode_selected) {
+ sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ return 0;
+ }
+
+ /*
+ * Nothing further to be done for platforms that support the low power
+ * D0 substate. Resume trace and return when resuming from
+ * low-power D0 substate
+ */
+ if (!runtime_resume && sof_ops(sdev)->set_power_state &&
+ old_state == SOF_DSP_PM_D0) {
+ ret = sof_fw_trace_resume(sdev);
+ if (ret < 0)
+ /* non fatal */
+ dev_warn(sdev->dev,
+ "failed to enable trace after resume %d\n", ret);
+ return 0;
+ }
+
+ if (override_on_demand_boot > -1)
+ on_demand_boot = override_on_demand_boot ? true : false;
+ else
+ on_demand_boot = sdev->pdata->desc->on_demand_dsp_boot;
+
+ if (on_demand_boot) {
+ /* Only change the fw_state to PREPARE but skip booting */
+ sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
+ return 0;
+ }
+
+ return snd_sof_boot_dsp_firmware(sdev);
+}
static int sof_suspend(struct device *dev, bool runtime_suspend)
{
{
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
- /* Notify DSP of upcoming power down */
- if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
+ /*
+ * Notify DSP of upcoming power down only if the firmware has been
+ * booted up
+ */
+ if (sdev->fw_state == SOF_FW_BOOT_COMPLETE && sof_ops(sdev)->remove &&
+ pm_ops && pm_ops->ctx_save)
return pm_ops->ctx_save(sdev);
return 0;