Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license.  When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2018 Intel Corporation. All rights reserved.
0007 //
0008 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
0009 //
0010 
0011 #include "ops.h"
0012 #include "sof-priv.h"
0013 #include "sof-audio.h"
0014 
0015 /*
0016  * Helper function to determine the target DSP state during
0017  * system suspend. This function only cares about the device
0018  * D-states. Platform-specific substates, if any, should be
0019  * handled by the platform-specific parts.
0020  */
0021 static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)
0022 {
0023     u32 target_dsp_state;
0024 
0025     switch (sdev->system_suspend_target) {
0026     case SOF_SUSPEND_S5:
0027     case SOF_SUSPEND_S4:
0028         /* DSP should be in D3 if the system is suspending to S3+ */
0029     case SOF_SUSPEND_S3:
0030         /* DSP should be in D3 if the system is suspending to S3 */
0031         target_dsp_state = SOF_DSP_PM_D3;
0032         break;
0033     case SOF_SUSPEND_S0IX:
0034         /*
0035          * Currently, the only criterion for retaining the DSP in D0
0036          * is that there are streams that ignored the suspend trigger.
0037          * Additional criteria such Soundwire clock-stop mode and
0038          * device suspend latency considerations will be added later.
0039          */
0040         if (snd_sof_stream_suspend_ignored(sdev))
0041             target_dsp_state = SOF_DSP_PM_D0;
0042         else
0043             target_dsp_state = SOF_DSP_PM_D3;
0044         break;
0045     default:
0046         /* This case would be during runtime suspend */
0047         target_dsp_state = SOF_DSP_PM_D3;
0048         break;
0049     }
0050 
0051     return target_dsp_state;
0052 }
0053 
0054 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
0055 static void sof_cache_debugfs(struct snd_sof_dev *sdev)
0056 {
0057     struct snd_sof_dfsentry *dfse;
0058 
0059     list_for_each_entry(dfse, &sdev->dfsentry_list, list) {
0060 
0061         /* nothing to do if debugfs buffer is not IO mem */
0062         if (dfse->type == SOF_DFSENTRY_TYPE_BUF)
0063             continue;
0064 
0065         /* cache memory that is only accessible in D0 */
0066         if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY)
0067             memcpy_fromio(dfse->cache_buf, dfse->io_mem,
0068                       dfse->size);
0069     }
0070 }
0071 #endif
0072 
0073 static int sof_resume(struct device *dev, bool runtime_resume)
0074 {
0075     struct snd_sof_dev *sdev = dev_get_drvdata(dev);
0076     const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
0077     const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
0078     u32 old_state = sdev->dsp_power_state.state;
0079     int ret;
0080 
0081     /* do nothing if dsp resume callbacks are not set */
0082     if (!runtime_resume && !sof_ops(sdev)->resume)
0083         return 0;
0084 
0085     if (runtime_resume && !sof_ops(sdev)->runtime_resume)
0086         return 0;
0087 
0088     /* DSP was never successfully started, nothing to resume */
0089     if (sdev->first_boot)
0090         return 0;
0091 
0092     /*
0093      * if the runtime_resume flag is set, call the runtime_resume routine
0094      * or else call the system resume routine
0095      */
0096     if (runtime_resume)
0097         ret = snd_sof_dsp_runtime_resume(sdev);
0098     else
0099         ret = snd_sof_dsp_resume(sdev);
0100     if (ret < 0) {
0101         dev_err(sdev->dev,
0102             "error: failed to power up DSP after resume\n");
0103         return ret;
0104     }
0105 
0106     /*
0107      * Nothing further to be done for platforms that support the low power
0108      * D0 substate. Resume trace and return when resuming from
0109      * low-power D0 substate
0110      */
0111     if (!runtime_resume && sof_ops(sdev)->set_power_state &&
0112         old_state == SOF_DSP_PM_D0) {
0113         ret = sof_fw_trace_resume(sdev);
0114         if (ret < 0)
0115             /* non fatal */
0116             dev_warn(sdev->dev,
0117                  "failed to enable trace after resume %d\n", ret);
0118         return 0;
0119     }
0120 
0121     sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
0122 
0123     /* load the firmware */
0124     ret = snd_sof_load_firmware(sdev);
0125     if (ret < 0) {
0126         dev_err(sdev->dev,
0127             "error: failed to load DSP firmware after resume %d\n",
0128             ret);
0129         sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
0130         return ret;
0131     }
0132 
0133     sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
0134 
0135     /*
0136      * Boot the firmware. The FW boot status will be modified
0137      * in snd_sof_run_firmware() depending on the outcome.
0138      */
0139     ret = snd_sof_run_firmware(sdev);
0140     if (ret < 0) {
0141         dev_err(sdev->dev,
0142             "error: failed to boot DSP firmware after resume %d\n",
0143             ret);
0144         sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
0145         return ret;
0146     }
0147 
0148     /* resume DMA trace */
0149     ret = sof_fw_trace_resume(sdev);
0150     if (ret < 0) {
0151         /* non fatal */
0152         dev_warn(sdev->dev,
0153              "warning: failed to init trace after resume %d\n",
0154              ret);
0155     }
0156 
0157     /* restore pipelines */
0158     if (tplg_ops->set_up_all_pipelines) {
0159         ret = tplg_ops->set_up_all_pipelines(sdev, false);
0160         if (ret < 0) {
0161             dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
0162             return ret;
0163         }
0164     }
0165 
0166     /* Notify clients not managed by pm framework about core resume */
0167     sof_resume_clients(sdev);
0168 
0169     /* notify DSP of system resume */
0170     if (pm_ops && pm_ops->ctx_restore) {
0171         ret = pm_ops->ctx_restore(sdev);
0172         if (ret < 0)
0173             dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
0174     }
0175 
0176     return ret;
0177 }
0178 
0179 static int sof_suspend(struct device *dev, bool runtime_suspend)
0180 {
0181     struct snd_sof_dev *sdev = dev_get_drvdata(dev);
0182     const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
0183     const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
0184     pm_message_t pm_state;
0185     u32 target_state = 0;
0186     int ret;
0187 
0188     /* do nothing if dsp suspend callback is not set */
0189     if (!runtime_suspend && !sof_ops(sdev)->suspend)
0190         return 0;
0191 
0192     if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
0193         return 0;
0194 
0195     if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
0196         goto suspend;
0197 
0198     /* prepare for streams to be resumed properly upon resume */
0199     if (!runtime_suspend) {
0200         ret = snd_sof_dsp_hw_params_upon_resume(sdev);
0201         if (ret < 0) {
0202             dev_err(sdev->dev,
0203                 "error: setting hw_params flag during suspend %d\n",
0204                 ret);
0205             return ret;
0206         }
0207     }
0208 
0209     target_state = snd_sof_dsp_power_target(sdev);
0210     pm_state.event = target_state;
0211 
0212     /* Skip to platform-specific suspend if DSP is entering D0 */
0213     if (target_state == SOF_DSP_PM_D0) {
0214         sof_fw_trace_suspend(sdev, pm_state);
0215         /* Notify clients not managed by pm framework about core suspend */
0216         sof_suspend_clients(sdev, pm_state);
0217         goto suspend;
0218     }
0219 
0220     if (tplg_ops->tear_down_all_pipelines)
0221         tplg_ops->tear_down_all_pipelines(sdev, false);
0222 
0223     /* suspend DMA trace */
0224     sof_fw_trace_suspend(sdev, pm_state);
0225 
0226     /* Notify clients not managed by pm framework about core suspend */
0227     sof_suspend_clients(sdev, pm_state);
0228 
0229 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
0230     /* cache debugfs contents during runtime suspend */
0231     if (runtime_suspend)
0232         sof_cache_debugfs(sdev);
0233 #endif
0234     /* notify DSP of upcoming power down */
0235     if (pm_ops && pm_ops->ctx_save) {
0236         ret = pm_ops->ctx_save(sdev);
0237         if (ret == -EBUSY || ret == -EAGAIN) {
0238             /*
0239              * runtime PM has logic to handle -EBUSY/-EAGAIN so
0240              * pass these errors up
0241              */
0242             dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);
0243             return ret;
0244         } else if (ret < 0) {
0245             /* FW in unexpected state, continue to power down */
0246             dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",
0247                  ret);
0248         }
0249     }
0250 
0251 suspend:
0252 
0253     /* return if the DSP was not probed successfully */
0254     if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
0255         return 0;
0256 
0257     /* platform-specific suspend */
0258     if (runtime_suspend)
0259         ret = snd_sof_dsp_runtime_suspend(sdev);
0260     else
0261         ret = snd_sof_dsp_suspend(sdev, target_state);
0262     if (ret < 0)
0263         dev_err(sdev->dev,
0264             "error: failed to power down DSP during suspend %d\n",
0265             ret);
0266 
0267     /* Do not reset FW state if DSP is in D0 */
0268     if (target_state == SOF_DSP_PM_D0)
0269         return ret;
0270 
0271     /* reset FW state */
0272     sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
0273     sdev->enabled_cores_mask = 0;
0274 
0275     return ret;
0276 }
0277 
0278 int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
0279 {
0280     const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
0281 
0282     /* Notify DSP of upcoming power down */
0283     if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
0284         return pm_ops->ctx_save(sdev);
0285 
0286     return 0;
0287 }
0288 
0289 int snd_sof_runtime_suspend(struct device *dev)
0290 {
0291     return sof_suspend(dev, true);
0292 }
0293 EXPORT_SYMBOL(snd_sof_runtime_suspend);
0294 
0295 int snd_sof_runtime_idle(struct device *dev)
0296 {
0297     struct snd_sof_dev *sdev = dev_get_drvdata(dev);
0298 
0299     return snd_sof_dsp_runtime_idle(sdev);
0300 }
0301 EXPORT_SYMBOL(snd_sof_runtime_idle);
0302 
0303 int snd_sof_runtime_resume(struct device *dev)
0304 {
0305     return sof_resume(dev, true);
0306 }
0307 EXPORT_SYMBOL(snd_sof_runtime_resume);
0308 
0309 int snd_sof_resume(struct device *dev)
0310 {
0311     return sof_resume(dev, false);
0312 }
0313 EXPORT_SYMBOL(snd_sof_resume);
0314 
0315 int snd_sof_suspend(struct device *dev)
0316 {
0317     return sof_suspend(dev, false);
0318 }
0319 EXPORT_SYMBOL(snd_sof_suspend);
0320 
0321 int snd_sof_prepare(struct device *dev)
0322 {
0323     struct snd_sof_dev *sdev = dev_get_drvdata(dev);
0324     const struct sof_dev_desc *desc = sdev->pdata->desc;
0325 
0326     /* will suspend to S3 by default */
0327     sdev->system_suspend_target = SOF_SUSPEND_S3;
0328 
0329     /*
0330      * if the firmware is crashed or boot failed then we try to aim for S3
0331      * to reboot the firmware
0332      */
0333     if (sdev->fw_state == SOF_FW_CRASHED ||
0334         sdev->fw_state == SOF_FW_BOOT_FAILED)
0335         return 0;
0336 
0337     if (!desc->use_acpi_target_states)
0338         return 0;
0339 
0340 #if defined(CONFIG_ACPI)
0341     switch (acpi_target_system_state()) {
0342     case ACPI_STATE_S0:
0343         sdev->system_suspend_target = SOF_SUSPEND_S0IX;
0344         break;
0345     case ACPI_STATE_S1:
0346     case ACPI_STATE_S2:
0347     case ACPI_STATE_S3:
0348         sdev->system_suspend_target = SOF_SUSPEND_S3;
0349         break;
0350     case ACPI_STATE_S4:
0351         sdev->system_suspend_target = SOF_SUSPEND_S4;
0352         break;
0353     case ACPI_STATE_S5:
0354         sdev->system_suspend_target = SOF_SUSPEND_S5;
0355         break;
0356     default:
0357         break;
0358     }
0359 #endif
0360 
0361     return 0;
0362 }
0363 EXPORT_SYMBOL(snd_sof_prepare);
0364 
0365 void snd_sof_complete(struct device *dev)
0366 {
0367     struct snd_sof_dev *sdev = dev_get_drvdata(dev);
0368 
0369     sdev->system_suspend_target = SOF_SUSPEND_NONE;
0370 }
0371 EXPORT_SYMBOL(snd_sof_complete);