ASoC: SOF: Move DSP power state transitions to platform-specific ops
[linux-2.6-microblaze.git] / sound / soc / sof / pm.c
index 84290bb..c410822 100644 (file)
 #include "sof-priv.h"
 #include "sof-audio.h"
 
+/*
+ * Helper function to determine the target DSP state during
+ * system suspend. This function only cares about the device
+ * D-states. Platform-specific substates, if any, should be
+ * handled by the platform-specific parts.
+ */
+static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)
+{
+       u32 target_dsp_state;
+
+       switch (sdev->system_suspend_target) {
+       case SOF_SUSPEND_S3:
+               /* DSP should be in D3 if the system is suspending to S3 */
+               target_dsp_state = SOF_DSP_PM_D3;
+               break;
+       case SOF_SUSPEND_S0IX:
+               /*
+                * Currently, the only criterion for retaining the DSP in D0
+                * is that there are streams that ignored the suspend trigger.
+                * Additional criteria such Soundwire clock-stop mode and
+                * device suspend latency considerations will be added later.
+                */
+               if (snd_sof_stream_suspend_ignored(sdev))
+                       target_dsp_state = SOF_DSP_PM_D0;
+               else
+                       target_dsp_state = SOF_DSP_PM_D3;
+               break;
+       default:
+               /* This case would be during runtime suspend */
+               target_dsp_state = SOF_DSP_PM_D3;
+               break;
+       }
+
+       return target_dsp_state;
+}
+
 static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
 {
        struct sof_ipc_pm_ctx pm_ctx;
@@ -50,12 +86,17 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev)
 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;
        int ret;
 
        /* do nothing if dsp resume callbacks are not set */
        if (!sof_ops(sdev)->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
@@ -70,6 +111,10 @@ static int sof_resume(struct device *dev, bool runtime_resume)
                return ret;
        }
 
+       /* Nothing further to do if resuming from a low-power D0 substate */
+       if (!runtime_resume && old_state == SOF_DSP_PM_D0)
+               return 0;
+
        sdev->fw_state = SOF_FW_BOOT_PREPARE;
 
        /* load the firmware */
@@ -120,15 +165,13 @@ static int sof_resume(struct device *dev, bool runtime_resume)
                        "error: ctx_restore ipc error during resume %d\n",
                        ret);
 
-       /* initialize default D0 sub-state */
-       sdev->d0_substate = SOF_DSP_D0I0;
-
        return ret;
 }
 
 static int sof_suspend(struct device *dev, bool runtime_suspend)
 {
        struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+       u32 target_state = 0;
        int ret;
 
        /* do nothing if dsp suspend callback is not set */
@@ -136,10 +179,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
                return 0;
 
        if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
-               goto power_down;
-
-       /* release trace */
-       snd_sof_release_trace(sdev);
+               goto suspend;
 
        /* set restore_stream for all streams during system suspend */
        if (!runtime_suspend) {
@@ -152,6 +192,15 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
                }
        }
 
+       target_state = snd_sof_dsp_power_target(sdev);
+
+       /* Skip to platform-specific suspend if DSP is entering D0 */
+       if (target_state == SOF_DSP_PM_D0)
+               goto suspend;
+
+       /* release trace */
+       snd_sof_release_trace(sdev);
+
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
        /* cache debugfs contents during runtime suspend */
        if (runtime_suspend)
@@ -175,22 +224,26 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
                         ret);
        }
 
-power_down:
+suspend:
 
        /* return if the DSP was not probed successfully */
        if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
                return 0;
 
-       /* power down all DSP cores */
+       /* platform-specific suspend */
        if (runtime_suspend)
                ret = snd_sof_dsp_runtime_suspend(sdev);
        else
-               ret = snd_sof_dsp_suspend(sdev);
+               ret = snd_sof_dsp_suspend(sdev, target_state);
        if (ret < 0)
                dev_err(sdev->dev,
                        "error: failed to power down DSP during suspend %d\n",
                        ret);
 
+       /* Do not reset FW state if DSP is in D0 */
+       if (target_state == SOF_DSP_PM_D0)
+               return ret;
+
        /* reset FW state */
        sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
 
@@ -217,112 +270,14 @@ int snd_sof_runtime_resume(struct device *dev)
 }
 EXPORT_SYMBOL(snd_sof_runtime_resume);
 
-int snd_sof_set_d0_substate(struct snd_sof_dev *sdev,
-                           enum sof_d0_substate d0_substate)
-{
-       int ret;
-
-       if (sdev->d0_substate == d0_substate)
-               return 0;
-
-       /* do platform specific set_state */
-       ret = snd_sof_dsp_set_power_state(sdev, d0_substate);
-       if (ret < 0)
-               return ret;
-
-       /* update dsp D0 sub-state */
-       sdev->d0_substate = d0_substate;
-
-       return 0;
-}
-EXPORT_SYMBOL(snd_sof_set_d0_substate);
-
-/*
- * Audio DSP states may transform as below:-
- *
- *                                         D0I3 compatible stream
- *     Runtime    +---------------------+   opened only, timeout
- *     suspend    |                     +--------------------+
- *   +------------+       D0(active)    |                    |
- *   |            |                     <---------------+    |
- *   |   +-------->                     |               |    |
- *   |   |Runtime +--^--+---------^--+--+ The last      |    |
- *   |   |resume     |  |         |  |    opened D0I3   |    |
- *   |   |           |  |         |  |    compatible    |    |
- *   |   |     resume|  |         |  |    stream closed |    |
- *   |   |      from |  | D3      |  |                  |    |
- *   |   |       D3  |  |suspend  |  | d0i3             |    |
- *   |   |           |  |         |  |suspend           |    |
- *   |   |           |  |         |  |                  |    |
- *   |   |           |  |         |  |                  |    |
- * +-v---+-----------+--v-------+ |  |           +------+----v----+
- * |                            | |  +----------->                |
- * |       D3 (suspended)       | |              |      D0I3      +-----+
- * |                            | +--------------+                |     |
- * |                            |  resume from   |                |     |
- * +-------------------^--------+  d0i3 suspend  +----------------+     |
- *                     |                                                |
- *                     |                       D3 suspend               |
- *                     +------------------------------------------------+
- *
- * d0i3_suspend = s0_suspend && D0I3 stream opened,
- * D3 suspend = !d0i3_suspend,
- */
-
 int snd_sof_resume(struct device *dev)
 {
-       struct snd_sof_dev *sdev = dev_get_drvdata(dev);
-       int ret;
-
-       if (snd_sof_dsp_d0i3_on_suspend(sdev)) {
-               /* resume from D0I3 */
-               dev_dbg(sdev->dev, "DSP will exit from D0i3...\n");
-               ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I0);
-               if (ret == -ENOTSUPP) {
-                       /* fallback to resume from D3 */
-                       dev_dbg(sdev->dev, "D0i3 not supported, fall back to resume from D3...\n");
-                       goto d3_resume;
-               } else if (ret < 0) {
-                       dev_err(sdev->dev, "error: failed to exit from D0I3 %d\n",
-                               ret);
-                       return ret;
-               }
-
-               /* platform-specific resume from D0i3 */
-               return snd_sof_dsp_resume(sdev);
-       }
-
-d3_resume:
-       /* resume from D3 */
        return sof_resume(dev, false);
 }
 EXPORT_SYMBOL(snd_sof_resume);
 
 int snd_sof_suspend(struct device *dev)
 {
-       struct snd_sof_dev *sdev = dev_get_drvdata(dev);
-       int ret;
-
-       if (snd_sof_dsp_d0i3_on_suspend(sdev)) {
-               /* suspend to D0i3 */
-               dev_dbg(sdev->dev, "DSP is trying to enter D0i3...\n");
-               ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I3);
-               if (ret == -ENOTSUPP) {
-                       /* fallback to D3 suspend */
-                       dev_dbg(sdev->dev, "D0i3 not supported, fall back to D3...\n");
-                       goto d3_suspend;
-               } else if (ret < 0) {
-                       dev_err(sdev->dev, "error: failed to enter D0I3, %d\n",
-                               ret);
-                       return ret;
-               }
-
-               /* platform-specific suspend to D0i3 */
-               return snd_sof_dsp_suspend(sdev);
-       }
-
-d3_suspend:
-       /* suspend to D3 */
        return sof_suspend(dev, false);
 }
 EXPORT_SYMBOL(snd_sof_suspend);
@@ -332,10 +287,13 @@ int snd_sof_prepare(struct device *dev)
        struct snd_sof_dev *sdev = dev_get_drvdata(dev);
 
 #if defined(CONFIG_ACPI)
-       sdev->s0_suspend = acpi_target_system_state() == ACPI_STATE_S0;
+       if (acpi_target_system_state() == ACPI_STATE_S0)
+               sdev->system_suspend_target = SOF_SUSPEND_S0IX;
+       else
+               sdev->system_suspend_target = SOF_SUSPEND_S3;
 #else
        /* will suspend to S3 by default */
-       sdev->s0_suspend = false;
+       sdev->system_suspend_target = SOF_SUSPEND_S3;
 #endif
 
        return 0;
@@ -346,6 +304,6 @@ void snd_sof_complete(struct device *dev)
 {
        struct snd_sof_dev *sdev = dev_get_drvdata(dev);
 
-       sdev->s0_suspend = false;
+       sdev->system_suspend_target = SOF_SUSPEND_NONE;
 }
 EXPORT_SYMBOL(snd_sof_complete);