From d0c266292765188d41b7ed09b080550cc6c74cca Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 5 Nov 2021 13:16:55 +0200 Subject: [PATCH] ASoC: SOF: Intel: hda: fix hotplug when only codec is suspended MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit If codec is in runtime suspend, but controller is not, hotplug events are missed as the codec has no way to alert the controller. Problem does not occur if both controller and codec are active, or when both are suspended. An easy way to reproduce is to play an audio stream on one codec (e.g. to HDMI/DP display codec), wait for other HDA codec to go to runtime suspend, and then plug in a headset to the suspended codec. The jack event is not reported correctly in this case. Another way to reproduce is to force controller to stay active with "snd_sof_pci.sof_pci_debug=0x1" Fix the issue by reconfiguring the WAKEEN register when powering up/down individual links, and handling control events in the interrupt handler. Fixes: 5e42205d2bda ("ASoC: SOF: Intel: hda: use hdac_ext fine-grained link management") Reported-by: Hui Wang Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Link: https://lore.kernel.org/r/20211105111655.668777-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-bus.c | 17 +++++++++++++++++ sound/soc/sof/intel/hda-dsp.c | 3 +-- sound/soc/sof/intel/hda.c | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c index 30025d3c16b6e..0862ff8b66273 100644 --- a/sound/soc/sof/intel/hda-bus.c +++ b/sound/soc/sof/intel/hda-bus.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include "../sof-priv.h" #include "hda.h" @@ -21,6 +23,18 @@ #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +static void update_codec_wake_enable(struct hdac_bus *bus, unsigned int addr, bool link_power) +{ + unsigned int mask = snd_hdac_chip_readw(bus, WAKEEN); + + if (link_power) + mask &= ~BIT(addr); + else + mask |= BIT(addr); + + snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask); +} + static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable) { struct hdac_bus *bus = codec->bus; @@ -41,6 +55,9 @@ static void sof_hda_bus_link_power(struct hdac_device *codec, bool enable) */ if (codec->addr == HDA_IDISP_ADDR && !enable) snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); + + /* WAKEEN needs to be set for disabled links */ + update_codec_wake_enable(bus, codec->addr, enable); } static const struct hdac_bus_ops bus_core_ops = { diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 058baca2cd0e9..287dc0eb6686f 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -622,8 +622,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) hda_dsp_ipc_int_disable(sdev); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - if (runtime_suspend) - hda_codec_jack_wake_enable(sdev, true); + hda_codec_jack_wake_enable(sdev, runtime_suspend); /* power down all hda link */ snd_hdac_ext_bus_link_power_down_all(bus); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 883d78dd01b5a..568d351b7a4e9 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -810,6 +810,20 @@ skip_soundwire: return 0; } +static void hda_check_for_state_change(struct snd_sof_dev *sdev) +{ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + struct hdac_bus *bus = sof_to_bus(sdev); + unsigned int codec_mask; + + codec_mask = snd_hdac_chip_readw(bus, STATESTS); + if (codec_mask) { + hda_codec_jack_check(sdev); + snd_hdac_chip_writew(bus, STATESTS, codec_mask); + } +#endif +} + static irqreturn_t hda_dsp_interrupt_handler(int irq, void *context) { struct snd_sof_dev *sdev = context; @@ -851,6 +865,8 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) if (hda_sdw_check_wakeen_irq(sdev)) hda_sdw_process_wakeen(sdev); + hda_check_for_state_change(sdev); + /* enable GIE interrupt */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, -- 2.39.5