162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 1062306a36Sopenharmony_ci// Rander Wang <rander.wang@intel.com> 1162306a36Sopenharmony_ci// Keyon Jie <yang.jie@linux.intel.com> 1262306a36Sopenharmony_ci// 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Hardware interface for generic Intel audio DSP HDA IP 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 2062306a36Sopenharmony_ci#include <sound/hda_register.h> 2162306a36Sopenharmony_ci#include <sound/hda-mlink.h> 2262306a36Sopenharmony_ci#include <trace/events/sof_intel.h> 2362306a36Sopenharmony_ci#include "../sof-audio.h" 2462306a36Sopenharmony_ci#include "../ops.h" 2562306a36Sopenharmony_ci#include "hda.h" 2662306a36Sopenharmony_ci#include "hda-ipc.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic bool hda_enable_trace_D0I3_S0; 2962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG) 3062306a36Sopenharmony_cimodule_param_named(enable_trace_D0I3_S0, hda_enable_trace_D0I3_S0, bool, 0444); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(enable_trace_D0I3_S0, 3262306a36Sopenharmony_ci "SOF HDA enable trace when the DSP is in D0I3 in S0"); 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * DSP Core control. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci u32 adspcs; 4262306a36Sopenharmony_ci u32 reset; 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* set reset bits for cores */ 4662306a36Sopenharmony_ci reset = HDA_DSP_ADSPCS_CRST_MASK(core_mask); 4762306a36Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 4862306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, 4962306a36Sopenharmony_ci reset, reset); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* poll with timeout to check if operation successful */ 5262306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 5362306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, adspcs, 5462306a36Sopenharmony_ci ((adspcs & reset) == reset), 5562306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 5662306a36Sopenharmony_ci HDA_DSP_RESET_TIMEOUT_US); 5762306a36Sopenharmony_ci if (ret < 0) { 5862306a36Sopenharmony_ci dev_err(sdev->dev, 5962306a36Sopenharmony_ci "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", 6062306a36Sopenharmony_ci __func__); 6162306a36Sopenharmony_ci return ret; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* has core entered reset ? */ 6562306a36Sopenharmony_ci adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 6662306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS); 6762306a36Sopenharmony_ci if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 6862306a36Sopenharmony_ci HDA_DSP_ADSPCS_CRST_MASK(core_mask)) { 6962306a36Sopenharmony_ci dev_err(sdev->dev, 7062306a36Sopenharmony_ci "error: reset enter failed: core_mask %x adspcs 0x%x\n", 7162306a36Sopenharmony_ci core_mask, adspcs); 7262306a36Sopenharmony_ci ret = -EIO; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned int crst; 8162306a36Sopenharmony_ci u32 adspcs; 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* clear reset bits for cores */ 8562306a36Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 8662306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, 8762306a36Sopenharmony_ci HDA_DSP_ADSPCS_CRST_MASK(core_mask), 8862306a36Sopenharmony_ci 0); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* poll with timeout to check if operation successful */ 9162306a36Sopenharmony_ci crst = HDA_DSP_ADSPCS_CRST_MASK(core_mask); 9262306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 9362306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, adspcs, 9462306a36Sopenharmony_ci !(adspcs & crst), 9562306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 9662306a36Sopenharmony_ci HDA_DSP_RESET_TIMEOUT_US); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (ret < 0) { 9962306a36Sopenharmony_ci dev_err(sdev->dev, 10062306a36Sopenharmony_ci "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", 10162306a36Sopenharmony_ci __func__); 10262306a36Sopenharmony_ci return ret; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* has core left reset ? */ 10662306a36Sopenharmony_ci adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 10762306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS); 10862306a36Sopenharmony_ci if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 0) { 10962306a36Sopenharmony_ci dev_err(sdev->dev, 11062306a36Sopenharmony_ci "error: reset leave failed: core_mask %x adspcs 0x%x\n", 11162306a36Sopenharmony_ci core_mask, adspcs); 11262306a36Sopenharmony_ci ret = -EIO; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* stall core */ 12162306a36Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 12262306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, 12362306a36Sopenharmony_ci HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), 12462306a36Sopenharmony_ci HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* set reset state */ 12762306a36Sopenharmony_ci return hda_dsp_core_reset_enter(sdev, core_mask); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cibool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int val; 13362306a36Sopenharmony_ci bool is_enable; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define MASK_IS_EQUAL(v, m, field) ({ \ 13862306a36Sopenharmony_ci u32 _m = field(m); \ 13962306a36Sopenharmony_ci ((v) & _m) == _m; \ 14062306a36Sopenharmony_ci}) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) && 14362306a36Sopenharmony_ci MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) && 14462306a36Sopenharmony_ci !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && 14562306a36Sopenharmony_ci !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#undef MASK_IS_EQUAL 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", 15062306a36Sopenharmony_ci is_enable, core_mask); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return is_enable; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* leave reset state */ 16062306a36Sopenharmony_ci ret = hda_dsp_core_reset_leave(sdev, core_mask); 16162306a36Sopenharmony_ci if (ret < 0) 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* run core */ 16562306a36Sopenharmony_ci dev_dbg(sdev->dev, "unstall/run core: core_mask = %x\n", core_mask); 16662306a36Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 16762306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, 16862306a36Sopenharmony_ci HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), 16962306a36Sopenharmony_ci 0); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* is core now running ? */ 17262306a36Sopenharmony_ci if (!hda_dsp_core_is_enabled(sdev, core_mask)) { 17362306a36Sopenharmony_ci hda_dsp_core_stall_reset(sdev, core_mask); 17462306a36Sopenharmony_ci dev_err(sdev->dev, "error: DSP start core failed: core_mask %x\n", 17562306a36Sopenharmony_ci core_mask); 17662306a36Sopenharmony_ci ret = -EIO; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Power Management. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciint hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 18962306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 19062306a36Sopenharmony_ci unsigned int cpa; 19162306a36Sopenharmony_ci u32 adspcs; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* restrict core_mask to host managed cores mask */ 19562306a36Sopenharmony_ci core_mask &= chip->host_managed_cores_mask; 19662306a36Sopenharmony_ci /* return if core_mask is not valid */ 19762306a36Sopenharmony_ci if (!core_mask) 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* update bits */ 20162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, 20262306a36Sopenharmony_ci HDA_DSP_ADSPCS_SPA_MASK(core_mask), 20362306a36Sopenharmony_ci HDA_DSP_ADSPCS_SPA_MASK(core_mask)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* poll with timeout to check if operation successful */ 20662306a36Sopenharmony_ci cpa = HDA_DSP_ADSPCS_CPA_MASK(core_mask); 20762306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 20862306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, adspcs, 20962306a36Sopenharmony_ci (adspcs & cpa) == cpa, 21062306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 21162306a36Sopenharmony_ci HDA_DSP_RESET_TIMEOUT_US); 21262306a36Sopenharmony_ci if (ret < 0) { 21362306a36Sopenharmony_ci dev_err(sdev->dev, 21462306a36Sopenharmony_ci "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", 21562306a36Sopenharmony_ci __func__); 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* did core power up ? */ 22062306a36Sopenharmony_ci adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, 22162306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS); 22262306a36Sopenharmony_ci if ((adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) != 22362306a36Sopenharmony_ci HDA_DSP_ADSPCS_CPA_MASK(core_mask)) { 22462306a36Sopenharmony_ci dev_err(sdev->dev, 22562306a36Sopenharmony_ci "error: power up core failed core_mask %xadspcs 0x%x\n", 22662306a36Sopenharmony_ci core_mask, adspcs); 22762306a36Sopenharmony_ci ret = -EIO; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci u32 adspcs; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* update bits */ 23962306a36Sopenharmony_ci snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, 24062306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, 24162306a36Sopenharmony_ci HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 24462306a36Sopenharmony_ci HDA_DSP_REG_ADSPCS, adspcs, 24562306a36Sopenharmony_ci !(adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)), 24662306a36Sopenharmony_ci HDA_DSP_REG_POLL_INTERVAL_US, 24762306a36Sopenharmony_ci HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC); 24862306a36Sopenharmony_ci if (ret < 0) 24962306a36Sopenharmony_ci dev_err(sdev->dev, 25062306a36Sopenharmony_ci "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", 25162306a36Sopenharmony_ci __func__); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciint hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 25962306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* restrict core_mask to host managed cores mask */ 26362306a36Sopenharmony_ci core_mask &= chip->host_managed_cores_mask; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* return if core_mask is not valid or cores are already enabled */ 26662306a36Sopenharmony_ci if (!core_mask || hda_dsp_core_is_enabled(sdev, core_mask)) 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* power up */ 27062306a36Sopenharmony_ci ret = hda_dsp_core_power_up(sdev, core_mask); 27162306a36Sopenharmony_ci if (ret < 0) { 27262306a36Sopenharmony_ci dev_err(sdev->dev, "error: dsp core power up failed: core_mask %x\n", 27362306a36Sopenharmony_ci core_mask); 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return hda_dsp_core_run(sdev, core_mask); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciint hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, 28162306a36Sopenharmony_ci unsigned int core_mask) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 28462306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 28562306a36Sopenharmony_ci int ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* restrict core_mask to host managed cores mask */ 28862306a36Sopenharmony_ci core_mask &= chip->host_managed_cores_mask; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* return if core_mask is not valid */ 29162306a36Sopenharmony_ci if (!core_mask) 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* place core in reset prior to power down */ 29562306a36Sopenharmony_ci ret = hda_dsp_core_stall_reset(sdev, core_mask); 29662306a36Sopenharmony_ci if (ret < 0) { 29762306a36Sopenharmony_ci dev_err(sdev->dev, "error: dsp core reset failed: core_mask %x\n", 29862306a36Sopenharmony_ci core_mask); 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* power down core */ 30362306a36Sopenharmony_ci ret = hda_dsp_core_power_down(sdev, core_mask); 30462306a36Sopenharmony_ci if (ret < 0) { 30562306a36Sopenharmony_ci dev_err(sdev->dev, "error: dsp core power down fail mask %x: %d\n", 30662306a36Sopenharmony_ci core_mask, ret); 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* make sure we are in OFF state */ 31162306a36Sopenharmony_ci if (hda_dsp_core_is_enabled(sdev, core_mask)) { 31262306a36Sopenharmony_ci dev_err(sdev->dev, "error: dsp core disable fail mask %x: %d\n", 31362306a36Sopenharmony_ci core_mask, ret); 31462306a36Sopenharmony_ci ret = -EIO; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_civoid hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 32362306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 32662306a36Sopenharmony_ci return; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* enable IPC DONE and BUSY interrupts */ 32962306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, 33062306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY, 33162306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* enable IPC interrupt */ 33462306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, 33562306a36Sopenharmony_ci HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_civoid hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 34162306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* disable IPC interrupt */ 34762306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, 34862306a36Sopenharmony_ci HDA_DSP_ADSPIC_IPC, 0); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* disable IPC BUSY and DONE interrupt */ 35162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, 35262306a36Sopenharmony_ci HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci int retry = HDA_DSP_REG_POLL_RETRY_COUNT; 35862306a36Sopenharmony_ci struct snd_sof_pdata *pdata = sdev->pdata; 35962306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci chip = get_chip_info(pdata); 36262306a36Sopenharmony_ci while (snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset) & 36362306a36Sopenharmony_ci SOF_HDA_VS_D0I3C_CIP) { 36462306a36Sopenharmony_ci if (!retry--) 36562306a36Sopenharmony_ci return -ETIMEDOUT; 36662306a36Sopenharmony_ci usleep_range(10, 15); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (pm_ops && pm_ops->set_pm_gate) 37762306a36Sopenharmony_ci return pm_ops->set_pm_gate(sdev, flags); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct snd_sof_pdata *pdata = sdev->pdata; 38562306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip; 38662306a36Sopenharmony_ci int ret; 38762306a36Sopenharmony_ci u8 reg; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci chip = get_chip_info(pdata); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Write to D0I3C after Command-In-Progress bit is cleared */ 39262306a36Sopenharmony_ci ret = hda_dsp_wait_d0i3c_done(sdev); 39362306a36Sopenharmony_ci if (ret < 0) { 39462306a36Sopenharmony_ci dev_err(sdev->dev, "CIP timeout before D0I3C update!\n"); 39562306a36Sopenharmony_ci return ret; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Update D0I3C register */ 39962306a36Sopenharmony_ci snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset, 40062306a36Sopenharmony_ci SOF_HDA_VS_D0I3C_I3, value); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * The value written to the D0I3C::I3 bit may not be taken into account immediately. 40462306a36Sopenharmony_ci * A delay is recommended before checking if D0I3C::CIP is cleared 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci usleep_range(30, 40); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Wait for cmd in progress to be cleared before exiting the function */ 40962306a36Sopenharmony_ci ret = hda_dsp_wait_d0i3c_done(sdev); 41062306a36Sopenharmony_ci if (ret < 0) { 41162306a36Sopenharmony_ci dev_err(sdev->dev, "CIP timeout after D0I3C update!\n"); 41262306a36Sopenharmony_ci return ret; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci reg = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, chip->d0i3_offset); 41662306a36Sopenharmony_ci /* Confirm d0i3 state changed with paranoia check */ 41762306a36Sopenharmony_ci if ((reg ^ value) & SOF_HDA_VS_D0I3C_I3) { 41862306a36Sopenharmony_ci dev_err(sdev->dev, "failed to update D0I3C!\n"); 41962306a36Sopenharmony_ci return -EIO; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci trace_sof_intel_D0I3C_updated(sdev, reg); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* 42862306a36Sopenharmony_ci * d0i3 streaming is enabled if all the active streams can 42962306a36Sopenharmony_ci * work in d0i3 state and playback is enabled 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_cistatic bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct snd_pcm_substream *substream; 43462306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 43562306a36Sopenharmony_ci bool playback_active = false; 43662306a36Sopenharmony_ci int dir; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 43962306a36Sopenharmony_ci for_each_pcm_streams(dir) { 44062306a36Sopenharmony_ci substream = spcm->stream[dir].substream; 44162306a36Sopenharmony_ci if (!substream || !substream->runtime) 44262306a36Sopenharmony_ci continue; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (!spcm->stream[dir].d0i3_compatible) 44562306a36Sopenharmony_ci return false; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (dir == SNDRV_PCM_STREAM_PLAYBACK) 44862306a36Sopenharmony_ci playback_active = true; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return playback_active; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, 45662306a36Sopenharmony_ci const struct sof_dsp_power_state *target_state) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci u32 flags = 0; 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci u8 value = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * Sanity check for illegal state transitions 46462306a36Sopenharmony_ci * The only allowed transitions are: 46562306a36Sopenharmony_ci * 1. D3 -> D0I0 46662306a36Sopenharmony_ci * 2. D0I0 -> D0I3 46762306a36Sopenharmony_ci * 3. D0I3 -> D0I0 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci switch (sdev->dsp_power_state.state) { 47062306a36Sopenharmony_ci case SOF_DSP_PM_D0: 47162306a36Sopenharmony_ci /* Follow the sequence below for D0 substate transitions */ 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case SOF_DSP_PM_D3: 47462306a36Sopenharmony_ci /* Follow regular flow for D3 -> D0 transition */ 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci default: 47762306a36Sopenharmony_ci dev_err(sdev->dev, "error: transition from %d to %d not allowed\n", 47862306a36Sopenharmony_ci sdev->dsp_power_state.state, target_state->state); 47962306a36Sopenharmony_ci return -EINVAL; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Set flags and register value for D0 target substate */ 48362306a36Sopenharmony_ci if (target_state->substate == SOF_HDA_DSP_PM_D0I3) { 48462306a36Sopenharmony_ci value = SOF_HDA_VS_D0I3C_I3; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* 48762306a36Sopenharmony_ci * Trace DMA need to be disabled when the DSP enters 48862306a36Sopenharmony_ci * D0I3 for S0Ix suspend, but it can be kept enabled 48962306a36Sopenharmony_ci * when the DSP enters D0I3 while the system is in S0 49062306a36Sopenharmony_ci * for debug purpose. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci if (!sdev->fw_trace_is_supported || 49362306a36Sopenharmony_ci !hda_enable_trace_D0I3_S0 || 49462306a36Sopenharmony_ci sdev->system_suspend_target != SOF_SUSPEND_NONE) 49562306a36Sopenharmony_ci flags = HDA_PM_NO_DMA_TRACE; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (hda_dsp_d0i3_streaming_applicable(sdev)) 49862306a36Sopenharmony_ci flags |= HDA_PM_PG_STREAMING; 49962306a36Sopenharmony_ci } else { 50062306a36Sopenharmony_ci /* prevent power gating in D0I0 */ 50162306a36Sopenharmony_ci flags = HDA_PM_PPG; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* update D0I3C register */ 50562306a36Sopenharmony_ci ret = hda_dsp_update_d0i3c_register(sdev, value); 50662306a36Sopenharmony_ci if (ret < 0) 50762306a36Sopenharmony_ci return ret; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * Notify the DSP of the state change. 51162306a36Sopenharmony_ci * If this IPC fails, revert the D0I3C register update in order 51262306a36Sopenharmony_ci * to prevent partial state change. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci ret = hda_dsp_send_pm_gate_ipc(sdev, flags); 51562306a36Sopenharmony_ci if (ret < 0) { 51662306a36Sopenharmony_ci dev_err(sdev->dev, 51762306a36Sopenharmony_ci "error: PM_GATE ipc error %d\n", ret); 51862306a36Sopenharmony_ci goto revert; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cirevert: 52462306a36Sopenharmony_ci /* fallback to the previous register value */ 52562306a36Sopenharmony_ci value = value ? 0 : SOF_HDA_VS_D0I3C_I3; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * This can fail but return the IPC error to signal that 52962306a36Sopenharmony_ci * the state change failed. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci hda_dsp_update_d0i3c_register(sdev, value); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return ret; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/* helper to log DSP state */ 53762306a36Sopenharmony_cistatic void hda_dsp_state_log(struct snd_sof_dev *sdev) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci switch (sdev->dsp_power_state.state) { 54062306a36Sopenharmony_ci case SOF_DSP_PM_D0: 54162306a36Sopenharmony_ci switch (sdev->dsp_power_state.substate) { 54262306a36Sopenharmony_ci case SOF_HDA_DSP_PM_D0I0: 54362306a36Sopenharmony_ci dev_dbg(sdev->dev, "Current DSP power state: D0I0\n"); 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci case SOF_HDA_DSP_PM_D0I3: 54662306a36Sopenharmony_ci dev_dbg(sdev->dev, "Current DSP power state: D0I3\n"); 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci default: 54962306a36Sopenharmony_ci dev_dbg(sdev->dev, "Unknown DSP D0 substate: %d\n", 55062306a36Sopenharmony_ci sdev->dsp_power_state.substate); 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci case SOF_DSP_PM_D1: 55562306a36Sopenharmony_ci dev_dbg(sdev->dev, "Current DSP power state: D1\n"); 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci case SOF_DSP_PM_D2: 55862306a36Sopenharmony_ci dev_dbg(sdev->dev, "Current DSP power state: D2\n"); 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci case SOF_DSP_PM_D3: 56162306a36Sopenharmony_ci dev_dbg(sdev->dev, "Current DSP power state: D3\n"); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci default: 56462306a36Sopenharmony_ci dev_dbg(sdev->dev, "Unknown DSP power state: %d\n", 56562306a36Sopenharmony_ci sdev->dsp_power_state.state); 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * All DSP power state transitions are initiated by the driver. 57262306a36Sopenharmony_ci * If the requested state change fails, the error is simply returned. 57362306a36Sopenharmony_ci * Further state transitions are attempted only when the set_power_save() op 57462306a36Sopenharmony_ci * is called again either because of a new IPC sent to the DSP or 57562306a36Sopenharmony_ci * during system suspend/resume. 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_cistatic int hda_dsp_set_power_state(struct snd_sof_dev *sdev, 57862306a36Sopenharmony_ci const struct sof_dsp_power_state *target_state) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci int ret = 0; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci switch (target_state->state) { 58362306a36Sopenharmony_ci case SOF_DSP_PM_D0: 58462306a36Sopenharmony_ci ret = hda_dsp_set_D0_state(sdev, target_state); 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci case SOF_DSP_PM_D3: 58762306a36Sopenharmony_ci /* The only allowed transition is: D0I0 -> D3 */ 58862306a36Sopenharmony_ci if (sdev->dsp_power_state.state == SOF_DSP_PM_D0 && 58962306a36Sopenharmony_ci sdev->dsp_power_state.substate == SOF_HDA_DSP_PM_D0I0) 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci dev_err(sdev->dev, 59362306a36Sopenharmony_ci "error: transition from %d to %d not allowed\n", 59462306a36Sopenharmony_ci sdev->dsp_power_state.state, target_state->state); 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci default: 59762306a36Sopenharmony_ci dev_err(sdev->dev, "error: target state unsupported %d\n", 59862306a36Sopenharmony_ci target_state->state); 59962306a36Sopenharmony_ci return -EINVAL; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci if (ret < 0) { 60262306a36Sopenharmony_ci dev_err(sdev->dev, 60362306a36Sopenharmony_ci "failed to set requested target DSP state %d substate %d\n", 60462306a36Sopenharmony_ci target_state->state, target_state->substate); 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci sdev->dsp_power_state = *target_state; 60962306a36Sopenharmony_ci hda_dsp_state_log(sdev); 61062306a36Sopenharmony_ci return ret; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciint hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev, 61462306a36Sopenharmony_ci const struct sof_dsp_power_state *target_state) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * When the DSP is already in D0I3 and the target state is D0I3, 61862306a36Sopenharmony_ci * it could be the case that the DSP is in D0I3 during S0 61962306a36Sopenharmony_ci * and the system is suspending to S0Ix. Therefore, 62062306a36Sopenharmony_ci * hda_dsp_set_D0_state() must be called to disable trace DMA 62162306a36Sopenharmony_ci * by sending the PM_GATE IPC to the FW. 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci if (target_state->substate == SOF_HDA_DSP_PM_D0I3 && 62462306a36Sopenharmony_ci sdev->system_suspend_target == SOF_SUSPEND_S0IX) 62562306a36Sopenharmony_ci return hda_dsp_set_power_state(sdev, target_state); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * For all other cases, return without doing anything if 62962306a36Sopenharmony_ci * the DSP is already in the target state. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci if (target_state->state == sdev->dsp_power_state.state && 63262306a36Sopenharmony_ci target_state->substate == sdev->dsp_power_state.substate) 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return hda_dsp_set_power_state(sdev, target_state); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ciint hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev, 63962306a36Sopenharmony_ci const struct sof_dsp_power_state *target_state) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci /* Return without doing anything if the DSP is already in the target state */ 64262306a36Sopenharmony_ci if (target_state->state == sdev->dsp_power_state.state && 64362306a36Sopenharmony_ci target_state->substate == sdev->dsp_power_state.substate) 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return hda_dsp_set_power_state(sdev, target_state); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/* 65062306a36Sopenharmony_ci * Audio DSP states may transform as below:- 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * Opportunistic D0I3 in S0 65362306a36Sopenharmony_ci * Runtime +---------------------+ Delayed D0i3 work timeout 65462306a36Sopenharmony_ci * suspend | +--------------------+ 65562306a36Sopenharmony_ci * +------------+ D0I0(active) | | 65662306a36Sopenharmony_ci * | | <---------------+ | 65762306a36Sopenharmony_ci * | +--------> | New IPC | | 65862306a36Sopenharmony_ci * | |Runtime +--^--+---------^--+--+ (via mailbox) | | 65962306a36Sopenharmony_ci * | |resume | | | | | | 66062306a36Sopenharmony_ci * | | | | | | | | 66162306a36Sopenharmony_ci * | | System| | | | | | 66262306a36Sopenharmony_ci * | | resume| | S3/S0IX | | | | 66362306a36Sopenharmony_ci * | | | | suspend | | S0IX | | 66462306a36Sopenharmony_ci * | | | | | |suspend | | 66562306a36Sopenharmony_ci * | | | | | | | | 66662306a36Sopenharmony_ci * | | | | | | | | 66762306a36Sopenharmony_ci * +-v---+-----------+--v-------+ | | +------+----v----+ 66862306a36Sopenharmony_ci * | | | +-----------> | 66962306a36Sopenharmony_ci * | D3 (suspended) | | | D0I3 | 67062306a36Sopenharmony_ci * | | +--------------+ | 67162306a36Sopenharmony_ci * | | System resume | | 67262306a36Sopenharmony_ci * +----------------------------+ +----------------+ 67362306a36Sopenharmony_ci * 67462306a36Sopenharmony_ci * S0IX suspend: The DSP is in D0I3 if any D0I3-compatible streams 67562306a36Sopenharmony_ci * ignored the suspend trigger. Otherwise the DSP 67662306a36Sopenharmony_ci * is in D3. 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 68262306a36Sopenharmony_ci const struct sof_intel_dsp_desc *chip = hda->desc; 68362306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 68462306a36Sopenharmony_ci int ret, j; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* 68762306a36Sopenharmony_ci * The memory used for IMR boot loses its content in deeper than S3 state 68862306a36Sopenharmony_ci * We must not try IMR boot on next power up (as it will fail). 68962306a36Sopenharmony_ci * 69062306a36Sopenharmony_ci * In case of firmware crash or boot failure set the skip_imr_boot to true 69162306a36Sopenharmony_ci * as well in order to try to re-load the firmware to do a 'cold' boot. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci if (sdev->system_suspend_target > SOF_SUSPEND_S3 || 69462306a36Sopenharmony_ci sdev->fw_state == SOF_FW_CRASHED || 69562306a36Sopenharmony_ci sdev->fw_state == SOF_FW_BOOT_FAILED) 69662306a36Sopenharmony_ci hda->skip_imr_boot = true; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci ret = chip->disable_interrupts(sdev); 69962306a36Sopenharmony_ci if (ret < 0) 70062306a36Sopenharmony_ci return ret; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci hda_codec_jack_wake_enable(sdev, runtime_suspend); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* power down all hda links */ 70562306a36Sopenharmony_ci hda_bus_ml_suspend(bus); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 70862306a36Sopenharmony_ci goto skip_dsp; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ret = chip->power_down_dsp(sdev); 71162306a36Sopenharmony_ci if (ret < 0) { 71262306a36Sopenharmony_ci dev_err(sdev->dev, "failed to power down DSP during suspend\n"); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* reset ref counts for all cores */ 71762306a36Sopenharmony_ci for (j = 0; j < chip->cores_num; j++) 71862306a36Sopenharmony_ci sdev->dsp_core_ref_count[j] = 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* disable ppcap interrupt */ 72162306a36Sopenharmony_ci hda_dsp_ctrl_ppcap_enable(sdev, false); 72262306a36Sopenharmony_ci hda_dsp_ctrl_ppcap_int_enable(sdev, false); 72362306a36Sopenharmony_ciskip_dsp: 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* disable hda bus irq and streams */ 72662306a36Sopenharmony_ci hda_dsp_ctrl_stop_chip(sdev); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* disable LP retention mode */ 72962306a36Sopenharmony_ci snd_sof_pci_update_bits(sdev, PCI_PGCTL, 73062306a36Sopenharmony_ci PCI_PGCTL_LSRMD_MASK, PCI_PGCTL_LSRMD_MASK); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* reset controller */ 73362306a36Sopenharmony_ci ret = hda_dsp_ctrl_link_reset(sdev, true); 73462306a36Sopenharmony_ci if (ret < 0) { 73562306a36Sopenharmony_ci dev_err(sdev->dev, 73662306a36Sopenharmony_ci "error: failed to reset controller during suspend\n"); 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* display codec can powered off after link reset */ 74162306a36Sopenharmony_ci hda_codec_i915_display_power(sdev, false); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci int ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* display codec must be powered before link reset */ 75162306a36Sopenharmony_ci hda_codec_i915_display_power(sdev, true); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * clear TCSEL to clear playback on some HD Audio 75562306a36Sopenharmony_ci * codecs. PCI TCSEL is defined in the Intel manuals. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* reset and start hda controller */ 76062306a36Sopenharmony_ci ret = hda_dsp_ctrl_init_chip(sdev); 76162306a36Sopenharmony_ci if (ret < 0) { 76262306a36Sopenharmony_ci dev_err(sdev->dev, 76362306a36Sopenharmony_ci "error: failed to start controller after resume\n"); 76462306a36Sopenharmony_ci goto cleanup; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* check jack status */ 76862306a36Sopenharmony_ci if (runtime_resume) { 76962306a36Sopenharmony_ci hda_codec_jack_wake_enable(sdev, false); 77062306a36Sopenharmony_ci if (sdev->system_suspend_target == SOF_SUSPEND_NONE) 77162306a36Sopenharmony_ci hda_codec_jack_check(sdev); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) { 77562306a36Sopenharmony_ci /* enable ppcap interrupt */ 77662306a36Sopenharmony_ci hda_dsp_ctrl_ppcap_enable(sdev, true); 77762306a36Sopenharmony_ci hda_dsp_ctrl_ppcap_int_enable(sdev, true); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cicleanup: 78162306a36Sopenharmony_ci /* display codec can powered off after controller init */ 78262306a36Sopenharmony_ci hda_codec_i915_display_power(sdev, false); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ciint hda_dsp_resume(struct snd_sof_dev *sdev) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 79062306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 79162306a36Sopenharmony_ci struct pci_dev *pci = to_pci_dev(sdev->dev); 79262306a36Sopenharmony_ci const struct sof_dsp_power_state target_state = { 79362306a36Sopenharmony_ci .state = SOF_DSP_PM_D0, 79462306a36Sopenharmony_ci .substate = SOF_HDA_DSP_PM_D0I0, 79562306a36Sopenharmony_ci }; 79662306a36Sopenharmony_ci int ret; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* resume from D0I3 */ 79962306a36Sopenharmony_ci if (sdev->dsp_power_state.state == SOF_DSP_PM_D0) { 80062306a36Sopenharmony_ci ret = hda_bus_ml_resume(bus); 80162306a36Sopenharmony_ci if (ret < 0) { 80262306a36Sopenharmony_ci dev_err(sdev->dev, 80362306a36Sopenharmony_ci "error %d in %s: failed to power up links", 80462306a36Sopenharmony_ci ret, __func__); 80562306a36Sopenharmony_ci return ret; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* set up CORB/RIRB buffers if was on before suspend */ 80962306a36Sopenharmony_ci hda_codec_resume_cmd_io(sdev); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Set DSP power state */ 81262306a36Sopenharmony_ci ret = snd_sof_dsp_set_power_state(sdev, &target_state); 81362306a36Sopenharmony_ci if (ret < 0) { 81462306a36Sopenharmony_ci dev_err(sdev->dev, "error: setting dsp state %d substate %d\n", 81562306a36Sopenharmony_ci target_state.state, target_state.substate); 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* restore L1SEN bit */ 82062306a36Sopenharmony_ci if (hda->l1_disabled) 82162306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, 82262306a36Sopenharmony_ci HDA_VS_INTEL_EM2, 82362306a36Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN, 0); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* restore and disable the system wakeup */ 82662306a36Sopenharmony_ci pci_restore_state(pci); 82762306a36Sopenharmony_ci disable_irq_wake(pci->irq); 82862306a36Sopenharmony_ci return 0; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* init hda controller. DSP cores will be powered up during fw boot */ 83262306a36Sopenharmony_ci ret = hda_resume(sdev, false); 83362306a36Sopenharmony_ci if (ret < 0) 83462306a36Sopenharmony_ci return ret; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return snd_sof_dsp_set_power_state(sdev, &target_state); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ciint hda_dsp_runtime_resume(struct snd_sof_dev *sdev) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci const struct sof_dsp_power_state target_state = { 84262306a36Sopenharmony_ci .state = SOF_DSP_PM_D0, 84362306a36Sopenharmony_ci }; 84462306a36Sopenharmony_ci int ret; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* init hda controller. DSP cores will be powered up during fw boot */ 84762306a36Sopenharmony_ci ret = hda_resume(sdev, true); 84862306a36Sopenharmony_ci if (ret < 0) 84962306a36Sopenharmony_ci return ret; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return snd_sof_dsp_set_power_state(sdev, &target_state); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ciint hda_dsp_runtime_idle(struct snd_sof_dev *sdev) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct hdac_bus *hbus = sof_to_bus(sdev); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (hbus->codec_powered) { 85962306a36Sopenharmony_ci dev_dbg(sdev->dev, "some codecs still powered (%08X), not idle\n", 86062306a36Sopenharmony_ci (unsigned int)hbus->codec_powered); 86162306a36Sopenharmony_ci return -EBUSY; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ciint hda_dsp_runtime_suspend(struct snd_sof_dev *sdev) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 87062306a36Sopenharmony_ci const struct sof_dsp_power_state target_state = { 87162306a36Sopenharmony_ci .state = SOF_DSP_PM_D3, 87262306a36Sopenharmony_ci }; 87362306a36Sopenharmony_ci int ret; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) { 87662306a36Sopenharmony_ci /* cancel any attempt for DSP D0I3 */ 87762306a36Sopenharmony_ci cancel_delayed_work_sync(&hda->d0i3_work); 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* stop hda controller and power dsp off */ 88162306a36Sopenharmony_ci ret = hda_suspend(sdev, true); 88262306a36Sopenharmony_ci if (ret < 0) 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci return snd_sof_dsp_set_power_state(sdev, &target_state); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ciint hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 89162306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 89262306a36Sopenharmony_ci struct pci_dev *pci = to_pci_dev(sdev->dev); 89362306a36Sopenharmony_ci const struct sof_dsp_power_state target_dsp_state = { 89462306a36Sopenharmony_ci .state = target_state, 89562306a36Sopenharmony_ci .substate = target_state == SOF_DSP_PM_D0 ? 89662306a36Sopenharmony_ci SOF_HDA_DSP_PM_D0I3 : 0, 89762306a36Sopenharmony_ci }; 89862306a36Sopenharmony_ci int ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) { 90162306a36Sopenharmony_ci /* cancel any attempt for DSP D0I3 */ 90262306a36Sopenharmony_ci cancel_delayed_work_sync(&hda->d0i3_work); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (target_state == SOF_DSP_PM_D0) { 90662306a36Sopenharmony_ci /* Set DSP power state */ 90762306a36Sopenharmony_ci ret = snd_sof_dsp_set_power_state(sdev, &target_dsp_state); 90862306a36Sopenharmony_ci if (ret < 0) { 90962306a36Sopenharmony_ci dev_err(sdev->dev, "error: setting dsp state %d substate %d\n", 91062306a36Sopenharmony_ci target_dsp_state.state, 91162306a36Sopenharmony_ci target_dsp_state.substate); 91262306a36Sopenharmony_ci return ret; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* enable L1SEN to make sure the system can enter S0Ix */ 91662306a36Sopenharmony_ci if (hda->l1_disabled) 91762306a36Sopenharmony_ci snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2, 91862306a36Sopenharmony_ci HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* stop the CORB/RIRB DMA if it is On */ 92162306a36Sopenharmony_ci hda_codec_suspend_cmd_io(sdev); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* no link can be powered in s0ix state */ 92462306a36Sopenharmony_ci ret = hda_bus_ml_suspend(bus); 92562306a36Sopenharmony_ci if (ret < 0) { 92662306a36Sopenharmony_ci dev_err(sdev->dev, 92762306a36Sopenharmony_ci "error %d in %s: failed to power down links", 92862306a36Sopenharmony_ci ret, __func__); 92962306a36Sopenharmony_ci return ret; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* enable the system waking up via IPC IRQ */ 93362306a36Sopenharmony_ci enable_irq_wake(pci->irq); 93462306a36Sopenharmony_ci pci_save_state(pci); 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* stop hda controller and power dsp off */ 93962306a36Sopenharmony_ci ret = hda_suspend(sdev, false); 94062306a36Sopenharmony_ci if (ret < 0) { 94162306a36Sopenharmony_ci dev_err(bus->dev, "error: suspending dsp\n"); 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 95162306a36Sopenharmony_ci struct hdac_stream *s; 95262306a36Sopenharmony_ci unsigned int active_streams = 0; 95362306a36Sopenharmony_ci int sd_offset; 95462306a36Sopenharmony_ci u32 val; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 95762306a36Sopenharmony_ci sd_offset = SOF_STREAM_SD_OFFSET(s); 95862306a36Sopenharmony_ci val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, 95962306a36Sopenharmony_ci sd_offset); 96062306a36Sopenharmony_ci if (val & SOF_HDA_SD_CTL_DMA_START) 96162306a36Sopenharmony_ci active_streams |= BIT(s->index); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return active_streams; 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci int ret; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Do not assume a certain timing between the prior 97362306a36Sopenharmony_ci * suspend flow, and running of this quirk function. 97462306a36Sopenharmony_ci * This is needed if the controller was just put 97562306a36Sopenharmony_ci * to reset before calling this function. 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci usleep_range(500, 1000); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* 98062306a36Sopenharmony_ci * Take controller out of reset to flush DMA 98162306a36Sopenharmony_ci * transactions. 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_ci ret = hda_dsp_ctrl_link_reset(sdev, false); 98462306a36Sopenharmony_ci if (ret < 0) 98562306a36Sopenharmony_ci return ret; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci usleep_range(500, 1000); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* Restore state for shutdown, back to reset */ 99062306a36Sopenharmony_ci ret = hda_dsp_ctrl_link_reset(sdev, true); 99162306a36Sopenharmony_ci if (ret < 0) 99262306a36Sopenharmony_ci return ret; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci return ret; 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ciint hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci unsigned int active_streams; 100062306a36Sopenharmony_ci int ret, ret2; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* check if DMA cleanup has been successful */ 100362306a36Sopenharmony_ci active_streams = hda_dsp_check_for_dma_streams(sdev); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci sdev->system_suspend_target = SOF_SUSPEND_S3; 100662306a36Sopenharmony_ci ret = snd_sof_suspend(sdev->dev); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (active_streams) { 100962306a36Sopenharmony_ci dev_warn(sdev->dev, 101062306a36Sopenharmony_ci "There were active DSP streams (%#x) at shutdown, trying to recover\n", 101162306a36Sopenharmony_ci active_streams); 101262306a36Sopenharmony_ci ret2 = hda_dsp_s5_quirk(sdev); 101362306a36Sopenharmony_ci if (ret2 < 0) 101462306a36Sopenharmony_ci dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2); 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return ret; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ciint hda_dsp_shutdown(struct snd_sof_dev *sdev) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci sdev->system_suspend_target = SOF_SUSPEND_S3; 102362306a36Sopenharmony_ci return snd_sof_suspend(sdev->dev); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ciint hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci int ret; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* make sure all DAI resources are freed */ 103162306a36Sopenharmony_ci ret = hda_dsp_dais_suspend(sdev); 103262306a36Sopenharmony_ci if (ret < 0) 103362306a36Sopenharmony_ci dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return ret; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_civoid hda_dsp_d0i3_work(struct work_struct *work) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct sof_intel_hda_dev *hdev = container_of(work, 104162306a36Sopenharmony_ci struct sof_intel_hda_dev, 104262306a36Sopenharmony_ci d0i3_work.work); 104362306a36Sopenharmony_ci struct hdac_bus *bus = &hdev->hbus.core; 104462306a36Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(bus->dev); 104562306a36Sopenharmony_ci struct sof_dsp_power_state target_state = { 104662306a36Sopenharmony_ci .state = SOF_DSP_PM_D0, 104762306a36Sopenharmony_ci .substate = SOF_HDA_DSP_PM_D0I3, 104862306a36Sopenharmony_ci }; 104962306a36Sopenharmony_ci int ret; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* DSP can enter D0I3 iff only D0I3-compatible streams are active */ 105262306a36Sopenharmony_ci if (!snd_sof_dsp_only_d0i3_compatible_stream_active(sdev)) 105362306a36Sopenharmony_ci /* remain in D0I0 */ 105462306a36Sopenharmony_ci return; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* This can fail but error cannot be propagated */ 105762306a36Sopenharmony_ci ret = snd_sof_dsp_set_power_state(sdev, &target_state); 105862306a36Sopenharmony_ci if (ret < 0) 105962306a36Sopenharmony_ci dev_err_ratelimited(sdev->dev, 106062306a36Sopenharmony_ci "error: failed to set DSP state %d substate %d\n", 106162306a36Sopenharmony_ci target_state.state, target_state.substate); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ciint hda_dsp_core_get(struct snd_sof_dev *sdev, int core) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; 106762306a36Sopenharmony_ci int ret, ret1; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* power up core */ 107062306a36Sopenharmony_ci ret = hda_dsp_enable_core(sdev, BIT(core)); 107162306a36Sopenharmony_ci if (ret < 0) { 107262306a36Sopenharmony_ci dev_err(sdev->dev, "failed to power up core %d with err: %d\n", 107362306a36Sopenharmony_ci core, ret); 107462306a36Sopenharmony_ci return ret; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* No need to send IPC for primary core or if FW boot is not complete */ 107862306a36Sopenharmony_ci if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE) 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* No need to continue the set_core_state ops is not available */ 108262306a36Sopenharmony_ci if (!pm_ops->set_core_state) 108362306a36Sopenharmony_ci return 0; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* Now notify DSP for secondary cores */ 108662306a36Sopenharmony_ci ret = pm_ops->set_core_state(sdev, core, true); 108762306a36Sopenharmony_ci if (ret < 0) { 108862306a36Sopenharmony_ci dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n", 108962306a36Sopenharmony_ci core, ret); 109062306a36Sopenharmony_ci goto power_down; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return ret; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cipower_down: 109662306a36Sopenharmony_ci /* power down core if it is host managed and return the original error if this fails too */ 109762306a36Sopenharmony_ci ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core)); 109862306a36Sopenharmony_ci if (ret1 < 0) 109962306a36Sopenharmony_ci dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return ret; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ciint hda_dsp_disable_interrupts(struct snd_sof_dev *sdev) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci hda_sdw_int_enable(sdev, false); 110762306a36Sopenharmony_ci hda_dsp_ipc_int_disable(sdev); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci} 1111