@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <sound/hdaudio_ext.h>
+#include <sound/hda_register.h>
#include <sound/hda_codec.h>
#include <sound/hda_i915.h>
#include <sound/sof.h>
@@ -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;
@@ -77,6 +117,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);
}
@@ -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) {
@@ -360,6 +373,10 @@ static int hda_resume(struct snd_sof_dev *sdev)
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)
@@ -407,19 +424,19 @@ static int hda_resume(struct snd_sof_dev *sdev)
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)
@@ -428,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;
@@ -398,6 +398,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)
@@ -556,6 +559,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 */