public inbox for linux-sound@vger.kernel.org
 help / color / mirror / Atom feed
From: "Cássio Gabriel" <cassiogabrielcontato@gmail.com>
To: Takashi Iwai <tiwai@suse.com>, Jaroslav Kysela <perex@perex.cz>
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Cássio Gabriel" <cassiogabrielcontato@gmail.com>
Subject: [PATCH 3/3] ALSA: interwave: add ISA and PnP suspend and resume callbacks
Date: Tue, 07 Apr 2026 12:22:51 -0300	[thread overview]
Message-ID: <20260407-alsa-interwave-pm-v1-3-80da1151f59c@gmail.com> (raw)
In-Reply-To: <20260407-alsa-interwave-pm-v1-0-80da1151f59c@gmail.com>

interwave still leaves both its ISA and PnP PM callbacks disabled even
though the shared GUS suspend and resume path now exists.

This board needs InterWave-specific glue around the shared GUS PM path.
The attached WSS codec has its own register image that must be saved and
restored across suspend, the InterWave-specific GF1 compatibility,
decode, MPU401, and emulation settings must be rewritten after the
shared GF1 resume path reinitializes the chip, and the probe-detected
InterWave memory layout must be restored without rerunning the
destructive DRAM/ROM detection path.

Track the optional STB TEA6330T bus at probe time, restore its cached
mixer state after resume, add resume-safe helpers for the InterWave
register and memory-configuration state, and wire both the ISA and PnP
front-ends up to the shared GUS PM helpers.

The resume path intentionally restores only the cached hardware setup.
It does not attempt to preserve sample RAM contents across suspend.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 sound/isa/gus/interwave.c | 178 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 151 insertions(+), 27 deletions(-)

diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 18adcd35e117..616c11e51a2f 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -96,6 +96,7 @@ struct snd_interwave {
 	struct snd_gus_card *gus;
 	struct snd_wss *wss;
 #ifdef SNDRV_STB
+	struct snd_i2c_bus *i2c_bus;
 	struct resource *i2c_res;
 #endif
 	unsigned short gus_status_reg;
@@ -363,18 +364,30 @@ struct rom_hdr {
 	/* 511 */ unsigned char csum;
 };
 
-static void snd_interwave_detect_memory(struct snd_gus_card *gus)
+static const unsigned int snd_interwave_memory_configs[] = {
+	0x00000001, 0x00000101, 0x01010101, 0x00000401,
+	0x04040401, 0x00040101, 0x04040101, 0x00000004,
+	0x00000404, 0x04040404, 0x00000010, 0x00001010,
+	0x10101010
+};
+
+static int snd_interwave_find_memory_config(unsigned int lmct)
 {
-	static const unsigned int lmc[13] =
-	{
-		0x00000001, 0x00000101, 0x01010101, 0x00000401,
-		0x04040401, 0x00040101, 0x04040101, 0x00000004,
-		0x00000404, 0x04040404, 0x00000010, 0x00001010,
-		0x10101010
-	};
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(snd_interwave_memory_configs); i++) {
+		if (lmct == snd_interwave_memory_configs[i])
+			return i;
+	}
+
+	return -EINVAL;
+}
 
+static void snd_interwave_detect_memory(struct snd_gus_card *gus)
+{
 	int bank_pos, pages;
 	unsigned int i, lmct;
+	int lmc_cfg;
 	int psizes[4];
 	unsigned char iwave[8];
 	unsigned char csum;
@@ -399,17 +412,20 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
 #if 0
 		dev_dbg(gus->card->dev, "lmct = 0x%08x\n", lmct);
 #endif
-		for (i = 0; i < ARRAY_SIZE(lmc); i++)
-			if (lmct == lmc[i]) {
+		lmc_cfg = snd_interwave_find_memory_config(lmct);
+		if (lmc_cfg >= 0) {
 #if 0
-				dev_dbg(gus->card->dev, "found !!! %i\n", i);
+			dev_dbg(gus->card->dev, "found !!! %i\n", lmc_cfg);
 #endif
-				snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
-				snd_interwave_bank_sizes(gus, psizes);
-				break;
-			}
-		if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode)
-			 snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2);
+			snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG,
+					(snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) |
+					lmc_cfg);
+			snd_interwave_bank_sizes(gus, psizes);
+		} else if (!gus->gf1.enh_mode) {
+			snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG,
+					(snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) |
+					2);
+		}
 		for (i = 0; i < 4; i++) {
 			gus->gf1.mem_alloc.banks_8[i].address =
 			    gus->gf1.mem_alloc.banks_16[i].address = i << 22;
@@ -454,24 +470,66 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
 		snd_interwave_reset(gus);
 }
 
+static void __snd_interwave_restore_regs(struct snd_gus_card *gus)
+{
+	snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
+}
+
+static void snd_interwave_restore_regs(struct snd_gus_card *gus)
+{
+	scoped_guard(spinlock_irqsave, &gus->reg_lock)
+		__snd_interwave_restore_regs(gus);
+}
+
+static void snd_interwave_restore_memory(struct snd_gus_card *gus)
+{
+	unsigned short mem_cfg;
+	unsigned int lmct = 0;
+	int i, lmc_cfg;
+
+	if (!gus->gf1.memory)
+		return;
+
+	for (i = 0; i < 4; i++)
+		lmct |= (gus->gf1.mem_alloc.banks_16[i].size >> 18) << (i * 8);
+
+	lmc_cfg = snd_interwave_find_memory_config(lmct);
+	if (lmc_cfg < 0) {
+		if (!gus->gf1.enh_mode) {
+			lmc_cfg = 2;
+		} else {
+			dev_warn(gus->card->dev,
+				 "cannot restore InterWave memory layout 0x%08x\n",
+				 lmct);
+			return;
+		}
+	}
+
+	scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+		mem_cfg = snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG);
+		mem_cfg = (mem_cfg & 0xfff0) | lmc_cfg;
+		mem_cfg = (mem_cfg & 0xff1f) | (4 << 5);
+		snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, mem_cfg);
+	}
+}
+
 static void snd_interwave_init(int dev, struct snd_gus_card *gus)
 {
-	/* ok.. some InterWave specific initialization */
+	/* Probe-time setup also clears the timer control register. */
 	scoped_guard(spinlock_irqsave, &gus->reg_lock) {
 		snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
-		snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
-		snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
-		snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
-		snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
-		snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
-		snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
+		__snd_interwave_restore_regs(gus);
 	}
 	gus->equal_irq = 1;
 	gus->codec_flag = 1;
 	gus->interwave = 1;
 	gus->max_flag = 1;
 	gus->joystick_dac = joystick_dac[dev];
-
 }
 
 static const struct snd_kcontrol_new snd_interwave_controls[] = {
@@ -724,6 +782,7 @@ static int snd_interwave_probe(struct snd_card *card, int dev,
 		err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1);
 		if (err < 0)
 			return err;
+		iwcard->i2c_bus = i2c_bus;
 	}
 #endif
 
@@ -828,10 +887,59 @@ static int snd_interwave_isa_probe(struct device *pdev,
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int snd_interwave_card_suspend(struct snd_card *card)
+{
+	struct snd_interwave *iwcard = card->private_data;
+
+	iwcard->wss->suspend(iwcard->wss);
+	return snd_gus_suspend(iwcard->gus);
+}
+
+static int snd_interwave_card_resume(struct snd_card *card)
+{
+	struct snd_interwave *iwcard = card->private_data;
+	int err;
+
+	err = snd_gus_resume(iwcard->gus);
+	if (err < 0)
+		return err;
+
+	snd_interwave_restore_regs(iwcard->gus);
+	snd_interwave_restore_memory(iwcard->gus);
+	iwcard->wss->resume(iwcard->wss);
+#ifdef SNDRV_STB
+	if (iwcard->i2c_bus) {
+		err = snd_tea6330t_restore_mixer(iwcard->i2c_bus);
+		if (err < 0)
+			dev_warn(card->dev,
+				 "failed to restore TEA6330T mixer state: %d\n",
+				 err);
+	}
+#endif
+
+	return 0;
+}
+
+static int snd_interwave_isa_suspend(struct device *pdev, unsigned int dev,
+				     pm_message_t state)
+{
+	return snd_interwave_card_suspend(dev_get_drvdata(pdev));
+}
+
+static int snd_interwave_isa_resume(struct device *pdev, unsigned int dev)
+{
+	return snd_interwave_card_resume(dev_get_drvdata(pdev));
+}
+#endif
+
 static struct isa_driver snd_interwave_driver = {
 	.match		= snd_interwave_isa_match,
 	.probe		= snd_interwave_isa_probe,
-	/* FIXME: suspend,resume */
+#ifdef CONFIG_PM
+	.suspend	= snd_interwave_isa_suspend,
+	.resume		= snd_interwave_isa_resume,
+#endif
 	.driver		= {
 		.name	= INTERWAVE_DRIVER
 	},
@@ -871,12 +979,28 @@ static int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int snd_interwave_pnpc_suspend(struct pnp_card_link *pcard,
+				      pm_message_t state)
+{
+	return snd_interwave_card_suspend(pnp_get_card_drvdata(pcard));
+}
+
+static int snd_interwave_pnpc_resume(struct pnp_card_link *pcard)
+{
+	return snd_interwave_card_resume(pnp_get_card_drvdata(pcard));
+}
+#endif
+
 static struct pnp_card_driver interwave_pnpc_driver = {
 	.flags = PNP_DRIVER_RES_DISABLE,
 	.name = INTERWAVE_PNP_DRIVER,
 	.id_table = snd_interwave_pnpids,
 	.probe = snd_interwave_pnp_detect,
-	/* FIXME: suspend,resume */
+#ifdef CONFIG_PM
+	.suspend	= snd_interwave_pnpc_suspend,
+	.resume		= snd_interwave_pnpc_resume,
+#endif
 };
 
 #endif /* CONFIG_PNP */

-- 
2.53.0


  parent reply	other threads:[~2026-04-07 15:23 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-07 15:22 [PATCH 0/3] ALSA: add suspend/resume support for InterWave ISA cards Cássio Gabriel
2026-04-07 15:22 ` [PATCH 1/3] ALSA: tea6330t: move snd_tea6330t_detect() EXPORT_SYMBOL Cássio Gabriel
2026-04-07 15:22 ` [PATCH 2/3] ALSA: tea6330t: add mixer state restore helper Cássio Gabriel
2026-04-07 15:22 ` Cássio Gabriel [this message]
2026-04-07 15:25 ` [PATCH 0/3] ALSA: add suspend/resume support for InterWave ISA cards Cássio Gabriel Monteiro Pires

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260407-alsa-interwave-pm-v1-3-80da1151f59c@gmail.com \
    --to=cassiogabrielcontato@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox