]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: SOF: Add support for on-demand DSP boot
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Mon, 15 Dec 2025 13:29:41 +0000 (15:29 +0200)
committerMark Brown <broonie@kernel.org>
Mon, 15 Dec 2025 14:07:41 +0000 (23:07 +0900)
On system suspend / resume we always power up the DSP and boot the
firmware, which is not strictly needed as right after the firmware booted
up we power the DSP down again on suspend and we also power it down after
resume after some inactivity.

Out of caution, add a new platform descriptor flag to enable on-demand
DSP boot since this might not work without changes to platform code on
certain platforms.

With the on-demand dsp boot enabled we will not boot the DSP and firmware
up on system or rpm resume, just enable audio subsystem since audio IPs,
like HDA and SoundWire might be needed (codecs suspend/resume operation).
Only boot up the DSP during the first hw_params() call when the DSP is
really going to be needed.

In this way we can handle the audio related use cases:
normal audio use (rpm suspend/resume)
system suspend/resume without active audio
system suspend/resume with active audio
system suspend/resume without active audio, and audio start before the rpm
suspend timeout

Add module option to force the on-demand DSP boot to allow it to be
disabled or enabled without kernel change for testing.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
Link: https://patch.msgid.link/20251215132946.2155-4-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/sof.h
sound/soc/sof/compress.c
sound/soc/sof/control.c
sound/soc/sof/core.c
sound/soc/sof/debug.c
sound/soc/sof/ipc3-dtrace.c
sound/soc/sof/ipc4.c
sound/soc/sof/pcm.c
sound/soc/sof/pm.c
sound/soc/sof/sof-priv.h

index eddea82c7b5a907433a55f200d115431e3a8e774..38d6c8cb5e832bac90a193556975bd3e8f24b611 100644 (file)
@@ -159,6 +159,9 @@ struct sof_dev_desc {
        /* The platform supports DSPless mode */
        bool dspless_mode_supported;
 
+       /* On demand DSP booting is possible on the platform */
+       bool on_demand_dsp_boot;
+
        /* defaults paths for firmware, library and topology files */
        const char *default_fw_path[SOF_IPC_TYPE_COUNT];
        const char *default_lib_path[SOF_IPC_TYPE_COUNT];
index 90b932ae3bab2c9483610e6c65a3ef3db239c104..86d563c864e57c61f57d134daccc9d2feb1c09df 100644 (file)
@@ -195,6 +195,14 @@ static int sof_compr_set_params(struct snd_soc_component *component,
        if (sizeof(*pcm) + ext_data_size > sdev->ipc->max_payload_size)
                return -EINVAL;
 
+       /*
+        * Make sure that the DSP is booted up, which might not be the
+        * case if the on-demand DSP boot is used
+        */
+       ret = snd_sof_boot_dsp_firmware(sdev);
+       if (ret)
+               return ret;
+
        pcm = kzalloc(sizeof(*pcm) + ext_data_size, GFP_KERNEL);
        if (!pcm)
                return -ENOMEM;
index 9582ab5f1113c3114bed28a134920c70dcea66bb..74d997a4f6203c73b3d358fc29bc586f65bd4d1b 100644 (file)
@@ -198,7 +198,12 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
                return ret;
        }
 
-       ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
+       /* Make sure the DSP/firmware is booted up */
+       ret = snd_sof_boot_dsp_firmware(sdev);
+       if (!ret)
+               ret = tplg_ops->control->bytes_ext_volatile_get(scontrol,
+                                                               binary_data,
+                                                               size);
 
        err = pm_runtime_put_autosuspend(scomp->dev);
        if (err < 0)
index b11f408f13664663e88f4793b0ca5844820c28c1..2d394389c945b499a343f16fe2e084bc20e1bbc0 100644 (file)
@@ -680,6 +680,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
        mutex_init(&sdev->power_state_access);
        mutex_init(&sdev->ipc_client_mutex);
        mutex_init(&sdev->client_event_handler_mutex);
+       mutex_init(&sdev->dsp_fw_boot_mutex);
 
        /* set default timeouts if none provided */
        if (plat_data->desc->ipc_timeout == 0)
index b24943a65c89fb12e4e5b8c589ccafd2dd57c090..6b9e1f1ee6574970c6c20b34e226bdaa5a3b2259 100644 (file)
@@ -216,7 +216,12 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
                goto error;
        }
 
-       ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
+       /* Make sure the DSP/firmware is booted up */
+       ret = snd_sof_boot_dsp_firmware(sdev);
+       if (!ret)
+               ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply,
+                                        SOF_IPC_MSG_MAX_SIZE);
+
        pm_runtime_put_autosuspend(sdev->dev);
        if (ret < 0 || reply->rhdr.error < 0) {
                ret = min(ret, reply->rhdr.error);
index 6ec391fd39a9029da5b58154d82e3783f5ae0518..50700f5cb0effd2db76a4b5126e36b25421aa0dc 100644 (file)
@@ -171,7 +171,12 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
                dev_err(sdev->dev, "enabling device failed: %d\n", ret);
                goto error;
        }
-       ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
+
+       /* Make sure the DSP/firmware is booted up */
+       ret = snd_sof_boot_dsp_firmware(sdev);
+       if (!ret)
+               ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
+
        pm_runtime_put_autosuspend(sdev->dev);
 
 error:
index a4a090e6724a6329e90d6a1dcf5154162ea4b0f4..1df97129cee627ac57fc2935c878cc827ba6ca92 100644 (file)
@@ -892,6 +892,19 @@ void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state)
        struct sof_ipc4_msg msg;
        u32 data = state;
 
+       /*
+        * The mic privacy change notification's role is to notify the running
+        * firmware that there is a change in mic privacy state from whatever
+        * the state was before - since the firmware booted up or since the
+        * previous change during runtime.
+        *
+        * If the firmware has not been booted up, there is no need to send
+        * change notification (the firmware is not booted up).
+        * The firmware checks the current state during its boot.
+        */
+       if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
+               return;
+
        msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
        msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
        msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
index cee04574264ebb0bd9c9d958a7a22200e574038e..31879a11c33ee792df999cb6d70dc3fff9fbb9cf 100644 (file)
@@ -122,6 +122,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
 
        spcm_dbg(spcm, substream->stream, "Entry: hw_params\n");
 
+       if (!sdev->dspless_mode_selected) {
+               /*
+                * Make sure that the DSP is booted up, which might not be the
+                * case if the on-demand DSP boot is used
+                */
+               ret = snd_sof_boot_dsp_firmware(sdev);
+               if (ret)
+                       return ret;
+       }
+
        /*
         * Handle repeated calls to hw_params() without free_pcm() in
         * between. At least ALSA OSS emulation depends on this.
index 8e3bcf602beb31a3eac2cbf5cfe252fbad9ebd61..dd7cd87f1fa5fd91392be79a759b9c1ad4ac5116 100644 (file)
@@ -8,10 +8,15 @@
 // 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
@@ -70,67 +75,28 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev)
 }
 #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;
        }
@@ -143,9 +109,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
         */
        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;
        }
@@ -154,16 +119,16 @@ static int sof_resume(struct device *dev, bool runtime_resume)
        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;
                }
        }
@@ -175,7 +140,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
        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:
@@ -192,6 +158,73 @@ 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)
 {
@@ -297,8 +330,12 @@ int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
 {
        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;
index 0f624d8cde201b2637c084adb2304d284225ea30..693d063830faeda9646a6a2e9534c6102c35e75a 100644 (file)
@@ -580,6 +580,8 @@ struct snd_sof_dev {
        wait_queue_head_t boot_wait;
        enum sof_fw_state fw_state;
        bool first_boot;
+       /* mutex to protect DSP firmware boot (except initial, probe time boot */
+       struct mutex dsp_fw_boot_mutex;
 
        /* work queue in case the probe is implemented in two steps */
        struct work_struct probe_work;
@@ -703,6 +705,7 @@ int snd_sof_suspend(struct device *dev);
 int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev);
 int snd_sof_prepare(struct device *dev);
 void snd_sof_complete(struct device *dev);
+int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev);
 
 void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);