[PATCH 2/2] ALSA: sscape: Add suspend and resume support

Cássio Gabriel
2026-04-11T04:54:44Z

The SoundScape ISA driver has lacked suspend and resume callbacks since
commit 277e926c9b27 ("[ALSA] sscape - Use platform_device").

A plain snd_wss resume is not sufficient for SoundScape. Resume also
needs to restore the board-specific gate-array routing, and non-VIVO
boards need to reinitialize the probe-time MIDI firmware and MIDI
control state when the MPU-401 side was enabled during probe.

That firmware reload can be handled in-kernel because
commit acd47100914b ("ALSA: sscape: convert to firmware loader framework")
moved the driver to request_firmware().

Add ISA and ISA-PnP PM callbacks, reconfigure the board on resume,
reload the non-VIVO MIDI firmware, restore the MIDI state, and then
resume the WSS codec. If MIDI firmware reload fails, keep the WSS resume
path alive and leave MIDI unavailable instead of failing the whole
device resume.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
sound/isa/sscape.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+)

diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 3e9465bc64ed..ff3d9e2498d9 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -144,6 +144,7 @@ struct soundscape {

unsigned char midi_vol;
bool joystick;
+ bool midi_enabled;
struct device *dev;
};

@@ -1103,6 +1104,7 @@ static int create_sscape(struct snd_card *card)
}

sscape->midi_vol = 0;
+ sscape->midi_enabled = true;
err = sscape_restore_midi_state(sscape);
if (err < 0)
dev_warn(card->dev,
@@ -1114,6 +1116,77 @@ static int create_sscape(struct snd_card *card)
return 0;
}

+#ifdef CONFIG_PM
+/*
+ * Reload the MIDI firmware and restore the saved MIDI state for
+ * boards whose MPU-401 side was enabled during probe.
+ */
+static int sscape_resume_midi(struct snd_card *card)
+{
+ struct soundscape *sscape = get_card_soundscape(card);
+ int err, version;
+
+ if (!sscape->midi_enabled)
+ return 0;
+
+ version = sscape_upload_bootblock(card);
+ if (version < 0)
+ return version;
+
+ err = sscape_upload_microcode(card, version);
+ if (err < 0)
+ return err;
+
+ outb(0, sscape->io_base);
+
+ return sscape_restore_midi_state(sscape);
+}
+
+/*
+ * Save the WSS codec state before the SoundScape is suspended.
+ */
+static int snd_sscape_suspend_card(struct snd_card *card)
+{
+ struct soundscape *sscape = get_card_soundscape(card);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ sscape->chip->suspend(sscape->chip);
+ return 0;
+}
+
+/*
+ * Restore the board-specific state before resuming the WSS codec.
+ */
+static int snd_sscape_resume_card(struct snd_card *card)
+{
+ struct soundscape *sscape = get_card_soundscape(card);
+ int err;
+
+ err = sscape_configure_board(sscape);
+ if (err < 0)
+ return err;
+
+ err = sscape_resume_midi(card);
+ if (err < 0)
+ dev_warn(card->dev, "sscape: MIDI restore failed: %d\n", err);
+
+ sscape->chip->resume(sscape->chip);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+
+static int snd_sscape_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ return snd_sscape_suspend_card(dev_get_drvdata(dev));
+}
+
+static int snd_sscape_resume(struct device *dev, unsigned int n)
+{
+ return snd_sscape_resume_card(dev_get_drvdata(dev));
+}
+#endif
+

static int snd_sscape_match(struct device *pdev, unsigned int i)
{
@@ -1170,6 +1243,10 @@ static int snd_sscape_probe(struct device *pdev, unsigned int dev)
static struct isa_driver snd_sscape_driver = {
.match = snd_sscape_match,
.probe = snd_sscape_probe,
+#ifdef CONFIG_PM
+ .suspend = snd_sscape_suspend,
+ .resume = snd_sscape_resume,
+#endif
.driver = {
.name = DEV_NAME
},
@@ -1267,11 +1344,27 @@ static int sscape_pnp_detect(struct pnp_card_link *pcard,
return 0;
}

+#ifdef CONFIG_PM
+static int sscape_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
+{
+ return snd_sscape_suspend_card(pnp_get_card_drvdata(pcard));
+}
+
+static int sscape_pnp_resume(struct pnp_card_link *pcard)
+{
+ return snd_sscape_resume_card(pnp_get_card_drvdata(pcard));
+}
+#endif
+
static struct pnp_card_driver sscape_pnpc_driver = {
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
.name = "sscape",
.id_table = sscape_pnpids,
.probe = sscape_pnp_detect,
+#ifdef CONFIG_PM
+ .suspend = sscape_pnp_suspend,
+ .resume = sscape_pnp_resume,
+#endif
};

#endif /* CONFIG_PNP */

--
2.53.0