diff options
Diffstat (limited to 'sound/soc/sof/intel/hda-dsp.c')
-rw-r--r-- | sound/soc/sof/intel/hda-dsp.c | 559 |
1 files changed, 370 insertions, 189 deletions
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index ed4d65a29d3a..31ffa1a8f2ac 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -18,6 +18,8 @@ #include <linux/module.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> +#include <sound/hda-mlink.h> +#include <trace/events/sof_intel.h> #include "../sof-audio.h" #include "../ops.h" #include "hda.h" @@ -34,7 +36,7 @@ MODULE_PARM_DESC(enable_trace_D0I3_S0, * DSP Core control. */ -int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) { u32 adspcs; u32 reset; @@ -44,7 +46,7 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) reset = HDA_DSP_ADSPCS_CRST_MASK(core_mask); snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, - reset, reset), + reset, reset); /* poll with timeout to check if operation successful */ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, @@ -73,7 +75,7 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) { unsigned int crst; u32 adspcs; @@ -125,6 +127,31 @@ int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) return hda_dsp_core_reset_enter(sdev, core_mask); } +bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + int val; + bool is_enable; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); + +#define MASK_IS_EQUAL(v, m, field) ({ \ + u32 _m = field(m); \ + ((v) & _m) == _m; \ +}) + + is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) && + MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) && + !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + +#undef MASK_IS_EQUAL + + dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", + is_enable, core_mask); + + return is_enable; +} + int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) { int ret; @@ -158,10 +185,18 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; unsigned int cpa; u32 adspcs; int ret; + /* restrict core_mask to host managed cores mask */ + core_mask &= chip->host_managed_cores_mask; + /* return if core_mask is not valid */ + if (!core_mask) + return 0; + /* update bits */ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, HDA_DSP_ADSPCS_SPA_MASK(core_mask), @@ -195,7 +230,7 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) { u32 adspcs; int ret; @@ -207,7 +242,7 @@ int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, adspcs, - !(adspcs & HDA_DSP_ADSPCS_SPA_MASK(core_mask)), + !(adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)), HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC); if (ret < 0) @@ -218,31 +253,17 @@ int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, - unsigned int core_mask) -{ - int val; - bool is_enable; - - val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); - - is_enable = (val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) && - (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) && - !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && - !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); - - dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", - is_enable, core_mask); - - return is_enable; -} - int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; int ret; - /* return if core is already enabled */ - if (hda_dsp_core_is_enabled(sdev, core_mask)) + /* restrict core_mask to host managed cores mask */ + core_mask &= chip->host_managed_cores_mask; + + /* return if core_mask is not valid or cores are already enabled */ + if (!core_mask || hda_dsp_core_is_enabled(sdev, core_mask)) return 0; /* power up */ @@ -259,8 +280,17 @@ int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; int ret; + /* restrict core_mask to host managed cores mask */ + core_mask &= chip->host_managed_cores_mask; + + /* return if core_mask is not valid */ + if (!core_mask) + return 0; + /* place core in reset prior to power down */ ret = hda_dsp_core_stall_reset(sdev, core_mask); if (ret < 0) { @@ -292,6 +322,9 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; + if (sdev->dspless_mode_selected) + return; + /* enable IPC DONE and BUSY interrupts */ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY, @@ -307,6 +340,9 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; + if (sdev->dspless_mode_selected) + return; + /* disable IPC interrupt */ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, HDA_DSP_ADSPIC_IPC, 0); @@ -318,10 +354,13 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev) { - struct hdac_bus *bus = sof_to_bus(sdev); int retry = HDA_DSP_REG_POLL_RETRY_COUNT; + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_intel_dsp_desc *chip; - while (snd_hdac_chip_readb(bus, VS_D0I3C) & SOF_HDA_VS_D0I3C_CIP) { + chip = get_chip_info(pdata); + while (snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset) & + SOF_HDA_VS_D0I3C_CIP) { if (!retry--) return -ETIMEDOUT; usleep_range(10, 15); @@ -332,50 +371,87 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev) static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags) { - struct sof_ipc_pm_gate pm_gate; - struct sof_ipc_reply reply; - - memset(&pm_gate, 0, sizeof(pm_gate)); + const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); - /* configure pm_gate ipc message */ - pm_gate.hdr.size = sizeof(pm_gate); - pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE; - pm_gate.flags = flags; + if (pm_ops && pm_ops->set_pm_gate) + return pm_ops->set_pm_gate(sdev, flags); - /* send pm_gate ipc to dsp */ - return sof_ipc_tx_message_no_pm(sdev->ipc, pm_gate.hdr.cmd, - &pm_gate, sizeof(pm_gate), &reply, - sizeof(reply)); + return 0; } static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) { - struct hdac_bus *bus = sof_to_bus(sdev); + struct snd_sof_pdata *pdata = sdev->pdata; + const struct sof_intel_dsp_desc *chip; int ret; + u8 reg; + + chip = get_chip_info(pdata); /* Write to D0I3C after Command-In-Progress bit is cleared */ ret = hda_dsp_wait_d0i3c_done(sdev); if (ret < 0) { - dev_err(bus->dev, "CIP timeout before D0I3C update!\n"); + dev_err(sdev->dev, "CIP timeout before D0I3C update!\n"); return ret; } /* Update D0I3C register */ - snd_hdac_chip_updateb(bus, VS_D0I3C, SOF_HDA_VS_D0I3C_I3, value); + snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset, + SOF_HDA_VS_D0I3C_I3, value); + + /* + * The value written to the D0I3C::I3 bit may not be taken into account immediately. + * A delay is recommended before checking if D0I3C::CIP is cleared + */ + usleep_range(30, 40); /* Wait for cmd in progress to be cleared before exiting the function */ ret = hda_dsp_wait_d0i3c_done(sdev); if (ret < 0) { - dev_err(bus->dev, "CIP timeout after D0I3C update!\n"); + dev_err(sdev->dev, "CIP timeout after D0I3C update!\n"); return ret; } - dev_vdbg(bus->dev, "D0I3C updated, register = 0x%x\n", - snd_hdac_chip_readb(bus, VS_D0I3C)); + reg = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset); + /* Confirm d0i3 state changed with paranoia check */ + if ((reg ^ value) & SOF_HDA_VS_D0I3C_I3) { + dev_err(sdev->dev, "failed to update D0I3C!\n"); + return -EIO; + } + + trace_sof_intel_D0I3C_updated(sdev, reg); return 0; } +/* + * d0i3 streaming is enabled if all the active streams can + * work in d0i3 state and playback is enabled + */ +static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev) +{ + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + bool playback_active = false; + int dir; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + substream = spcm->stream[dir].substream; + if (!substream || !substream->runtime) + continue; + + if (!spcm->stream[dir].d0i3_compatible) + return false; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + playback_active = true; + } + } + + return playback_active; +} + static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { @@ -413,10 +489,13 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, * when the DSP enters D0I3 while the system is in S0 * for debug purpose. */ - if (!sdev->dtrace_is_supported || + if (!sdev->fw_trace_is_supported || !hda_enable_trace_D0I3_S0 || sdev->system_suspend_target != SOF_SUSPEND_NONE) flags = HDA_PM_NO_DMA_TRACE; + + if (hda_dsp_d0i3_streaming_applicable(sdev)) + flags |= HDA_PM_PG_STREAMING; } else { /* prevent power gating in D0I0 */ flags = HDA_PM_PPG; @@ -478,15 +557,9 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev) case SOF_DSP_PM_D2: dev_dbg(sdev->dev, "Current DSP power state: D2\n"); break; - case SOF_DSP_PM_D3_HOT: - dev_dbg(sdev->dev, "Current DSP power state: D3_HOT\n"); - break; case SOF_DSP_PM_D3: dev_dbg(sdev->dev, "Current DSP power state: D3\n"); break; - case SOF_DSP_PM_D3_COLD: - dev_dbg(sdev->dev, "Current DSP power state: D3_COLD\n"); - break; default: dev_dbg(sdev->dev, "Unknown DSP power state: %d\n", sdev->dsp_power_state.state); @@ -501,31 +574,11 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev) * is called again either because of a new IPC sent to the DSP or * during system suspend/resume. */ -int hda_dsp_set_power_state(struct snd_sof_dev *sdev, - const struct sof_dsp_power_state *target_state) +static int hda_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) { int ret = 0; - /* - * When the DSP is already in D0I3 and the target state is D0I3, - * it could be the case that the DSP is in D0I3 during S0 - * and the system is suspending to S0Ix. Therefore, - * hda_dsp_set_D0_state() must be called to disable trace DMA - * by sending the PM_GATE IPC to the FW. - */ - if (target_state->substate == SOF_HDA_DSP_PM_D0I3 && - sdev->system_suspend_target == SOF_SUSPEND_S0IX) - goto set_state; - - /* - * For all other cases, return without doing anything if - * the DSP is already in the target state. - */ - if (target_state->state == sdev->dsp_power_state.state && - target_state->substate == sdev->dsp_power_state.substate) - return 0; - -set_state: switch (target_state->state) { case SOF_DSP_PM_D0: ret = hda_dsp_set_D0_state(sdev, target_state); @@ -557,6 +610,42 @@ set_state: return ret; } +int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + /* + * When the DSP is already in D0I3 and the target state is D0I3, + * it could be the case that the DSP is in D0I3 during S0 + * and the system is suspending to S0Ix. Therefore, + * hda_dsp_set_D0_state() must be called to disable trace DMA + * by sending the PM_GATE IPC to the FW. + */ + if (target_state->substate == SOF_HDA_DSP_PM_D0I3 && + sdev->system_suspend_target == SOF_SUSPEND_S0IX) + return hda_dsp_set_power_state(sdev, target_state); + + /* + * For all other cases, return without doing anything if + * the DSP is already in the target state. + */ + if (target_state->state == sdev->dsp_power_state.state && + target_state->substate == sdev->dsp_power_state.substate) + return 0; + + return hda_dsp_set_power_state(sdev, target_state); +} + +int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + /* Return without doing anything if the DSP is already in the target state */ + if (target_state->state == sdev->dsp_power_state.state && + target_state->substate == sdev->dsp_power_state.substate) + return 0; + + return hda_dsp_set_power_state(sdev, target_state); +} + /* * Audio DSP states may transform as below:- * @@ -591,35 +680,50 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_bus *bus = sof_to_bus(sdev); -#endif - int ret; + int ret, j; - hda_sdw_int_enable(sdev, false); + /* + * The memory used for IMR boot loses its content in deeper than S3 state + * We must not try IMR boot on next power up (as it will fail). + * + * In case of firmware crash or boot failure set the skip_imr_boot to true + * as well in order to try to re-load the firmware to do a 'cold' boot. + */ + if (sdev->system_suspend_target > SOF_SUSPEND_S3 || + sdev->fw_state == SOF_FW_CRASHED || + sdev->fw_state == SOF_FW_BOOT_FAILED) + hda->skip_imr_boot = true; - /* disable IPC interrupts */ - hda_dsp_ipc_int_disable(sdev); + ret = chip->disable_interrupts(sdev); + if (ret < 0) + return ret; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - if (runtime_suspend) - hda_codec_jack_wake_enable(sdev); + /* make sure that no irq handler is pending before shutdown */ + synchronize_irq(sdev->ipc_irq); - /* power down all hda link */ - snd_hdac_ext_bus_link_power_down_all(bus); -#endif + hda_codec_jack_wake_enable(sdev, runtime_suspend); + + /* power down all hda links */ + hda_bus_ml_suspend(bus); - /* power down DSP */ - ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + if (sdev->dspless_mode_selected) + goto skip_dsp; + + ret = chip->power_down_dsp(sdev); if (ret < 0) { - dev_err(sdev->dev, - "error: failed to power down core during suspend\n"); + dev_err(sdev->dev, "failed to power down DSP during suspend\n"); return ret; } + /* reset ref counts for all cores */ + for (j = 0; j < chip->cores_num; j++) + sdev->dsp_core_ref_count[j] = 0; + /* disable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, false); hda_dsp_ctrl_ppcap_int_enable(sdev, false); +skip_dsp: /* disable hda bus irq and streams */ hda_dsp_ctrl_stop_chip(sdev); @@ -644,10 +748,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_ext_link *hlink = NULL; -#endif + const struct sof_intel_dsp_desc *chip; int ret; /* display codec must be powered before link reset */ @@ -660,32 +761,33 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); /* reset and start hda controller */ - ret = hda_dsp_ctrl_init_chip(sdev, true); + ret = hda_dsp_ctrl_init_chip(sdev); if (ret < 0) { dev_err(sdev->dev, "error: failed to start controller after resume\n"); - return ret; + goto cleanup; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* check jack status */ - if (runtime_resume) - hda_codec_jack_check(sdev); + if (runtime_resume) { + hda_codec_jack_wake_enable(sdev, false); + if (sdev->system_suspend_target == SOF_SUSPEND_NONE) + hda_codec_jack_check(sdev); + } - /* turn off the links that were off before suspend */ - list_for_each_entry(hlink, &bus->hlink_list, list) { - if (!hlink->ref_count) - snd_hdac_ext_bus_link_power_down(hlink); + if (!sdev->dspless_mode_selected) { + /* enable ppcap interrupt */ + hda_dsp_ctrl_ppcap_enable(sdev, true); + hda_dsp_ctrl_ppcap_int_enable(sdev, true); } - /* check dma status and clean up CORB/RIRB buffers */ - if (!bus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(bus); -#endif + chip = get_chip_info(sdev->pdata); + if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) + hda_sdw_int_enable(sdev, true); - /* enable ppcap interrupt */ - hda_dsp_ctrl_ppcap_enable(sdev, true); - hda_dsp_ctrl_ppcap_int_enable(sdev, true); +cleanup: + /* display codec can powered off after controller init */ + hda_codec_i915_display_power(sdev, false); return 0; } @@ -693,39 +795,26 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) int hda_dsp_resume(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = to_pci_dev(sdev->dev); const struct sof_dsp_power_state target_state = { .state = SOF_DSP_PM_D0, .substate = SOF_HDA_DSP_PM_D0I0, }; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_ext_link *hlink = NULL; -#endif int ret; /* resume from D0I3 */ if (sdev->dsp_power_state.state == SOF_DSP_PM_D0) { - hda_codec_i915_display_power(sdev, true); - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - /* power up links that were active before suspend */ - list_for_each_entry(hlink, &bus->hlink_list, list) { - if (hlink->ref_count) { - ret = snd_hdac_ext_bus_link_power_up(hlink); - if (ret < 0) { - dev_dbg(sdev->dev, - "error %x in %s: failed to power up links", - ret, __func__); - return ret; - } - } + ret = hda_bus_ml_resume(bus); + if (ret < 0) { + dev_err(sdev->dev, + "error %d in %s: failed to power up links", + ret, __func__); + return ret; } /* set up CORB/RIRB buffers if was on before suspend */ - if (bus->cmd_dma_state) - snd_hdac_bus_init_cmd_io(bus); -#endif + hda_codec_resume_cmd_io(sdev); /* Set DSP power state */ ret = snd_sof_dsp_set_power_state(sdev, &target_state); @@ -736,7 +825,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev) } /* restore L1SEN bit */ - if (hda->l1_support_changed) + if (hda->l1_disabled) snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2, HDA_VS_INTEL_EM2_L1SEN, 0); @@ -785,11 +874,17 @@ int hda_dsp_runtime_idle(struct snd_sof_dev *sdev) int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_dsp_power_state target_state = { .state = SOF_DSP_PM_D3, }; int ret; + if (!sdev->dspless_mode_selected) { + /* cancel any attempt for DSP D0I3 */ + cancel_delayed_work_sync(&hda->d0i3_work); + } + /* stop hda controller and power dsp off */ ret = hda_suspend(sdev, true); if (ret < 0) @@ -810,13 +905,12 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) }; int ret; - /* cancel any attempt for DSP D0I3 */ - cancel_delayed_work_sync(&hda->d0i3_work); + if (!sdev->dspless_mode_selected) { + /* cancel any attempt for DSP D0I3 */ + cancel_delayed_work_sync(&hda->d0i3_work); + } if (target_state == SOF_DSP_PM_D0) { - /* we can't keep a wakeref to display driver at suspend */ - hda_codec_i915_display_power(sdev, false); - /* Set DSP power state */ ret = snd_sof_dsp_set_power_state(sdev, &target_dsp_state); if (ret < 0) { @@ -827,26 +921,21 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) } /* enable L1SEN to make sure the system can enter S0Ix */ - hda->l1_support_changed = - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - HDA_VS_INTEL_EM2, - HDA_VS_INTEL_EM2_L1SEN, - HDA_VS_INTEL_EM2_L1SEN); + if (hda->l1_disabled) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2, + HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* stop the CORB/RIRB DMA if it is On */ - if (bus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(bus); + hda_codec_suspend_cmd_io(sdev); /* no link can be powered in s0ix state */ - ret = snd_hdac_ext_bus_link_power_down_all(bus); + ret = hda_bus_ml_suspend(bus); if (ret < 0) { - dev_dbg(sdev->dev, + dev_err(sdev->dev, "error %d in %s: failed to power down links", ret, __func__); return ret; } -#endif /* enable the system waking up via IPC IRQ */ enable_irq_wake(pci->irq); @@ -864,46 +953,94 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } -int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) +static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_bus *bus = sof_to_bus(sdev); - struct snd_soc_pcm_runtime *rtd; - struct hdac_ext_stream *stream; - struct hdac_ext_link *link; struct hdac_stream *s; - const char *name; - int stream_tag; + unsigned int active_streams = 0; + int sd_offset; + u32 val; - /* set internal flag for BE */ list_for_each_entry(s, &bus->stream_list, list) { - stream = stream_to_hdac_ext_stream(s); + sd_offset = SOF_STREAM_SD_OFFSET(s); + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + sd_offset); + if (val & SOF_HDA_SD_CTL_DMA_START) + active_streams |= BIT(s->index); + } - /* - * clear stream. This should already be taken care for running - * streams when the SUSPEND trigger is called. But paused - * streams do not get suspended, so this needs to be done - * explicitly during suspend. - */ - if (stream->link_substream) { - rtd = asoc_substream_to_rtd(stream->link_substream); - name = asoc_rtd_to_codec(rtd, 0)->component->name; - link = snd_hdac_ext_bus_get_link(bus, name); - if (!link) - return -EINVAL; + return active_streams; +} - stream->link_prepared = 0; +static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) +{ + int ret; - if (hdac_stream(stream)->direction == - SNDRV_PCM_STREAM_CAPTURE) - continue; + /* + * Do not assume a certain timing between the prior + * suspend flow, and running of this quirk function. + * This is needed if the controller was just put + * to reset before calling this function. + */ + usleep_range(500, 1000); - stream_tag = hdac_stream(stream)->stream_tag; - snd_hdac_ext_link_clear_stream_id(link, stream_tag); - } + /* + * Take controller out of reset to flush DMA + * transactions. + */ + ret = hda_dsp_ctrl_link_reset(sdev, false); + if (ret < 0) + return ret; + + usleep_range(500, 1000); + + /* Restore state for shutdown, back to reset */ + ret = hda_dsp_ctrl_link_reset(sdev, true); + if (ret < 0) + return ret; + + return ret; +} + +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) +{ + unsigned int active_streams; + int ret, ret2; + + /* check if DMA cleanup has been successful */ + active_streams = hda_dsp_check_for_dma_streams(sdev); + + sdev->system_suspend_target = SOF_SUSPEND_S3; + ret = snd_sof_suspend(sdev->dev); + + if (active_streams) { + dev_warn(sdev->dev, + "There were active DSP streams (%#x) at shutdown, trying to recover\n", + active_streams); + ret2 = hda_dsp_s5_quirk(sdev); + if (ret2 < 0) + dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2); } -#endif - return 0; + + return ret; +} + +int hda_dsp_shutdown(struct snd_sof_dev *sdev) +{ + sdev->system_suspend_target = SOF_SUSPEND_S3; + return snd_sof_suspend(sdev->dev); +} + +int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) +{ + int ret; + + /* make sure all DAI resources are freed */ + ret = hda_dsp_dais_suspend(sdev); + if (ret < 0) + dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__); + + return ret; } void hda_dsp_d0i3_work(struct work_struct *work) @@ -913,19 +1050,15 @@ void hda_dsp_d0i3_work(struct work_struct *work) d0i3_work.work); struct hdac_bus *bus = &hdev->hbus.core; struct snd_sof_dev *sdev = dev_get_drvdata(bus->dev); - struct sof_dsp_power_state target_state; + struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + .substate = SOF_HDA_DSP_PM_D0I3, + }; int ret; - target_state.state = SOF_DSP_PM_D0; - /* DSP can enter D0I3 iff only D0I3-compatible streams are active */ - if (snd_sof_dsp_only_d0i3_compatible_stream_active(sdev)) - target_state.substate = SOF_HDA_DSP_PM_D0I3; - else - target_state.substate = SOF_HDA_DSP_PM_D0I0; - - /* remain in D0I0 */ - if (target_state.substate == SOF_HDA_DSP_PM_D0I0) + if (!snd_sof_dsp_only_d0i3_compatible_stream_active(sdev)) + /* remain in D0I0 */ return; /* This can fail but error cannot be propagated */ @@ -935,3 +1068,51 @@ void hda_dsp_d0i3_work(struct work_struct *work) "error: failed to set DSP state %d substate %d\n", target_state.state, target_state.substate); } + +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + int ret, ret1; + + /* power up core */ + ret = hda_dsp_enable_core(sdev, BIT(core)); + if (ret < 0) { + dev_err(sdev->dev, "failed to power up core %d with err: %d\n", + core, ret); + return ret; + } + + /* No need to send IPC for primary core or if FW boot is not complete */ + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE) + return 0; + + /* No need to continue the set_core_state ops is not available */ + if (!pm_ops->set_core_state) + return 0; + + /* Now notify DSP for secondary cores */ + ret = pm_ops->set_core_state(sdev, core, true); + if (ret < 0) { + dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n", + core, ret); + goto power_down; + } + + return ret; + +power_down: + /* power down core if it is host managed and return the original error if this fails too */ + ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core)); + if (ret1 < 0) + dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1); + + return ret; +} + +int hda_dsp_disable_interrupts(struct snd_sof_dev *sdev) +{ + hda_sdw_int_enable(sdev, false); + hda_dsp_ipc_int_disable(sdev); + + return 0; +} |