]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: interwave: add ISA and PnP suspend and resume callbacks
authorCássio Gabriel <cassiogabrielcontato@gmail.com>
Tue, 7 Apr 2026 15:35:43 +0000 (12:35 -0300)
committerTakashi Iwai <tiwai@suse.de>
Wed, 8 Apr 2026 07:55:03 +0000 (09:55 +0200)
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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20260407-alsa-interwave-pm-v2-3-8dd96c6129e9@gmail.com
sound/isa/gus/interwave.c

index 18adcd35e117baf666b16018f6449b5e85f4662f..616c11e51a2f609e99c9888f209256201225f8c3 100644 (file)
@@ -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 */