[PATCH v2 3/3] ALSA: interwave: add ISA and PnP suspend and resume callbacks

Cássio Gabriel
2026-04-07T15:36:02Z

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