mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 13:53:33 +00:00
ASoC: SOF: Intel: hda: fix hotplug when only codec is suspended
[ Upstream commitfd572393ba
] 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:87fc20e4a0
("ASoC: SOF: Intel: hda: use hdac_ext fine-grained link management") Reported-by: Hui Wang <hui.wang@canonical.com> Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> Link: https://lore.kernel.org/r/20211105111655.668777-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
7b3a34f08d
commit
37c8d485cb
3 changed files with 34 additions and 2 deletions
|
@ -10,6 +10,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include <sound/hda_codec.h>
|
||||
#include <sound/hda_register.h>
|
||||
#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 = {
|
||||
|
|
|
@ -623,8 +623,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);
|
||||
|
|
|
@ -696,6 +696,20 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
|
|||
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;
|
||||
|
@ -737,6 +751,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,
|
||||
|
|
Loading…
Reference in a new issue