ALSA: sscape: Add suspend and resume support

The SoundScape ISA driver has lacked suspend and resume callbacks since
commit 277e926c9b ("[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 acd4710091 ("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>
Link: https://patch.msgid.link/20260411-alsa-sscape-pm-v2-2-aeb5682e14b0@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Cássio Gabriel
2026-04-11 15:14:41 -03:00
committed by Takashi Iwai
parent f312f8b598
commit 713e0f0111

View File

@@ -144,6 +144,7 @@ struct soundscape {
unsigned char midi_vol;
bool joystick;
bool midi_enabled;
struct device *dev;
};
@@ -1107,6 +1108,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,
@@ -1118,6 +1120,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)
{
@@ -1174,6 +1247,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
},
@@ -1271,11 +1348,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 */