[PATCH] ALSA: hda: intel: reset link for HP ENVY 27-p014

Casey Tunturi via B4 Relay <[email protected]>
Newsgroups gmane.linux.sound,gmane.linux.kernel
Message-ID <20260828-hp-envy-27-hda-reset-v1-1-0da50c215148@samaritansolutions.net>
From: Casey Tunturi <[email protected]>

While bringing up audio on an HP ENVY 27-p014, I found that the normal
link reset only discovers the HDMI codec. The CX20753/4 at address 0
appears after a second CRST pulse, with STATESTS left alone and no fixed
delays.

I added that pulse when address 0 is missing, and again before the codecs
resume. Initial setup uses immediate commands long enough to add the
codec, then returns the controller to CORB/RIRB.

That brought analog audio up at boot, and capture kept working through
four deep suspend and resume cycles.

Signed-off-by: Casey Tunturi <[email protected]>
---
 sound/hda/controllers/intel.c | 77 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
index 24015c73a..0be97a46f 100644
--- a/sound/hda/controllers/intel.c
+++ b/sound/hda/controllers/intel.c
@@ -987,6 +987,46 @@ static bool azx_is_pm_ready(struct snd_card *card)
 	return true;
 }
 
+static bool hp_envy_27_needs_reset(struct azx *chip)
+{
+	struct pci_dev *pci = chip->pci;
+
+	return pci->subsystem_vendor == 0x103c &&
+	       pci->subsystem_device == 0x2b3e;
+}
+
+static u16 hp_envy_27_reset_link(struct azx *chip)
+{
+	struct hdac_bus *bus = azx_bus(chip);
+	u32 gctl;
+	u16 statests;
+	int timeout;
+
+	snd_hdac_bus_stop_chip(bus);
+
+	gctl = snd_hdac_chip_readl(bus, GCTL);
+	snd_hdac_chip_writel(bus, GCTL, gctl & ~AZX_GCTL_RESET);
+	for (timeout = 250; timeout; timeout--) {
+		if (!(snd_hdac_chip_readl(bus, GCTL) & AZX_GCTL_RESET))
+			break;
+		udelay(1);
+	}
+
+	gctl = snd_hdac_chip_readl(bus, GCTL);
+	snd_hdac_chip_writel(bus, GCTL, gctl | AZX_GCTL_RESET);
+	for (timeout = 250; timeout; timeout--) {
+		if (snd_hdac_chip_readl(bus, GCTL) & AZX_GCTL_RESET)
+			break;
+		udelay(1);
+	}
+
+	statests = snd_hdac_chip_readw(bus, STATESTS);
+	bus->codec_mask |= statests;
+	snd_hdac_bus_init_chip(bus, false);
+
+	return statests;
+}
+
 static void __azx_runtime_resume(struct azx *chip)
 {
 	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
@@ -1003,6 +1043,8 @@ static void __azx_runtime_resume(struct azx *chip)
 
 	azx_init_pci(chip);
 	hda_intel_init_chip(chip, true);
+	if (chip->pm_prepared && hp_envy_27_needs_reset(chip))
+		hp_envy_27_reset_link(chip);
 
 	/* Avoid codec resume if runtime resume is for system suspend */
 	if (!chip->pm_prepared) {
@@ -2356,6 +2398,34 @@ static const unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
 	[AZX_DRIVER_TERA] = 1,
 };
 
+static int hp_envy_27_probe_codec(struct azx *chip)
+{
+	struct hdac_bus *bus = azx_bus(chip);
+	struct hda_codec *codec = NULL;
+	bool use_pio;
+	int probe_mask;
+	int err;
+
+	if (!hp_envy_27_needs_reset(chip) || (bus->codec_mask & BIT(0)))
+		return 0;
+	if (!(hp_envy_27_reset_link(chip) & BIT(0)))
+		return -ENODEV;
+
+	probe_mask = chip->codec_probe_mask;
+	use_pio = bus->use_pio_for_commands;
+	chip->codec_probe_mask = BIT(0);
+	bus->use_pio_for_commands = true;
+	err = snd_hda_codec_new(&chip->bus, chip->card, 0, &codec);
+	chip->codec_probe_mask = probe_mask;
+	if (!err && codec)
+		err = snd_hda_codec_configure(codec);
+	else if (!err)
+		err = -ENODEV;
+	bus->use_pio_for_commands = use_pio;
+
+	return err;
+}
+
 static int azx_probe_continue(struct azx *chip)
 {
 	struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
@@ -2428,6 +2498,11 @@ static int azx_probe_continue(struct azx *chip)
 		}
 	}
 
+	err = hp_envy_27_probe_codec(chip);
+	if (err < 0 && err != -ENODEV)
+		dev_warn(chip->card->dev,
+			 "failed to probe HP ENVY 27 analog codec: %d\n", err);
+
 	err = snd_card_register(chip->card);
 	if (err < 0)
 		goto out_free;
@@ -2444,6 +2519,8 @@ static int azx_probe_continue(struct azx *chip)
 		pm_runtime_allow(&pci->dev);
 		pm_runtime_put_autosuspend(&pci->dev);
 	}
+	if (hp_envy_27_needs_reset(chip))
+		pm_runtime_forbid(&pci->dev);
 
 out_free:
 	if (err < 0) {

---
base-commit: c4a0927f535f779700d5ccda8182c2db01e9d551
change-id: 20260828-hp-envy-27-hda-reset-40651087e528

Best regards,
--  
Casey Tunturi <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.