diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 0d8437b080bfa5..5882f1bfd906ca 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -37,16 +38,55 @@ static void hda_codec_load_module(struct hda_codec *codec) static void hda_codec_load_module(struct hda_codec *codec) {} #endif +/* check jack status after resuming from suspend mode */ +void hda_codec_jack_check(struct snd_sof_dev *sdev, int status) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct hda_bus *hbus = sof_to_hbus(sdev); + struct hdac_bus *bus = sof_to_bus(sdev); + struct hda_codec *codec; + int mask; + + /* + * there are two reasons for runtime resume + * (1) waken up by interrupt triggered by WAKEEN feature + * (2) waken up by pm get functions for some audio operations + * For case (1), the bits in status mean which codec triggers + * the interrupt and jacks will be checked on these codecs. + * For case (2), we need to check all the non-hdmi codecs for some + * cases like playback with HDMI or capture with DMIC. In these + * cases, only controller is active and codecs are suspended, so + * codecs can't send unsolicited event to controller. The jack polling + * operation will activate codecs and unsolicited event can work + * even codecs become suspended later. + */ + mask = status ? status : hda->hda_codec_mask; + + list_for_each_codec(codec, hbus) + if (mask & BIT(codec->core.addr)) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + + /* disable controller Wake Up event*/ + snd_hdac_chip_writew(bus, WAKEEN, + snd_hdac_chip_readw(bus, WAKEEN) & + ~hda->hda_codec_mask); +} +#else +void hda_codec_jack_check(struct snd_sof_dev *sdev, int status) {} #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +EXPORT_SYMBOL(hda_codec_jack_check); /* probe individual codec */ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) { - struct hda_bus *hbus = sof_to_hbus(sdev); - struct hdac_device *hdev; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_hda_priv *hda_priv; #endif + struct hda_bus *hbus = sof_to_hbus(sdev); + struct hdac_device *hdev; + u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; u32 resp = -1; @@ -76,6 +116,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) /* use legacy bus only for HDA codecs, idisp uses ext bus */ if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) { hdev->type = HDA_DEV_LEGACY; + hda->hda_codec_mask |= BIT(address); hda_codec_load_module(&hda_priv->codec); } diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index f2c5a12db930a4..f8ed8a4a60b789 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -282,7 +282,8 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0); } -static int hda_suspend(struct snd_sof_dev *sdev, int state) +static int hda_suspend(struct snd_sof_dev *sdev, int state, + bool runtime_suspend) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -295,6 +296,12 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state) hda_dsp_ipc_int_disable(sdev); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + if (IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) && runtime_suspend) + /* enable controller wake up event */ + snd_hdac_chip_writew(bus, WAKEEN, + snd_hdac_chip_readw(bus, WAKEEN) | + hda->hda_codec_mask); + /* power down all hda link */ snd_hdac_ext_bus_link_power_down_all(bus); #endif @@ -329,11 +336,12 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state) return 0; } -static int hda_resume(struct snd_sof_dev *sdev) +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; + int status; #endif int ret; @@ -344,6 +352,11 @@ static int hda_resume(struct snd_sof_dev *sdev) snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + if (runtime_resume) { + /* read STATESTS before controller reset */ + status = snd_hdac_chip_readw(bus, STATESTS); + } + /* reset and start hda controller */ ret = hda_dsp_ctrl_init_chip(sdev, true); if (ret < 0) { @@ -359,6 +372,20 @@ static int hda_resume(struct snd_sof_dev *sdev) bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); hda_dsp_ctrl_misc_clock_gating(sdev, true); + + /* check jack status based on controller status */ + if (runtime_resume) + hda_codec_jack_check(sdev, status); + + /* 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); + } + + /* check dma status and clean up CORB/RIRB buffers */ + if (!bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); #else hda_dsp_ctrl_misc_clock_gating(sdev, false); @@ -391,37 +418,25 @@ static int hda_resume(struct snd_sof_dev *sdev) hda_dsp_ctrl_ppcap_enable(sdev, true); hda_dsp_ctrl_ppcap_int_enable(sdev, true); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - /* 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); - } - - /* check dma status and clean up CORB/RIRB buffers */ - if (!bus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(bus); -#endif - return 0; } int hda_dsp_resume(struct snd_sof_dev *sdev) { /* init hda controller. DSP cores will be powered up during fw boot */ - return hda_resume(sdev); + return hda_resume(sdev, false); } int hda_dsp_runtime_resume(struct snd_sof_dev *sdev) { /* init hda controller. DSP cores will be powered up during fw boot */ - return hda_resume(sdev); + return hda_resume(sdev, true); } int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state) { /* stop hda controller and power dsp off */ - return hda_suspend(sdev, state); + return hda_suspend(sdev, state, true); } int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) @@ -430,7 +445,7 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) int ret; /* stop hda controller and power dsp off */ - ret = hda_suspend(sdev, state); + ret = hda_suspend(sdev, state, false); if (ret < 0) { dev_err(bus->dev, "error: suspending dsp\n"); return ret; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 50653859e0a044..aa69a4024b9da1 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -392,6 +392,9 @@ struct sof_intel_hda_dev { /* DMIC device */ struct platform_device *dmic_dev; + + /* hda codec mask excluding hdmi */ + u32 hda_codec_mask; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -550,6 +553,7 @@ void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev, * HDA Codec operations. */ int hda_codec_probe_bus(struct snd_sof_dev *sdev); +void hda_codec_jack_check(struct snd_sof_dev *sdev, int status); #endif /* CONFIG_SND_SOC_SOF_HDA */