[PATCH 2/2] ALSA: msnd: add ISA and PnP system sleep callbacks

From: Cássio Gabriel

Date: Thu Apr 09 2026 - 01:08:49 EST


The msnd drivers do not implement system sleep callbacks today, so
they have no defined way to recover DSP state after suspend.

Add common card suspend/resume helpers, rerun the DSP
initialization path on resume, restore the cached capture-source
state, and rearm the shared IRQ for already-open users.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@xxxxxxxxx>
---
sound/isa/msnd/msnd.h | 2 +
sound/isa/msnd/msnd_pinnacle.c | 95 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 95 insertions(+), 2 deletions(-)

diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
index b25beca25c0d..56a700e6a5cb 100644
--- a/sound/isa/msnd/msnd.h
+++ b/sound/isa/msnd/msnd.h
@@ -253,6 +253,8 @@ struct snd_msnd {
spinlock_t mixer_lock;
int nresets;
unsigned recsrc;
+ u8 pm_recsrc;
+ bool pm_mpu_input;
#define LEVEL_ENTRIES 32
int left_levels[LEVEL_ENTRIES];
int right_levels[LEVEL_ENTRIES];
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index c4eec391cd29..5b729bb02ef6 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -513,6 +513,19 @@ static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
snd_msnd_disable_irq(mpu->private_data);
}

+#ifdef CONFIG_PM
+static u8 snd_msnd_pm_recsrc(struct snd_msnd *chip)
+{
+ /* Convert recsrc to the Capture Source selector: 0=Analog, 1=MASS, 2=SPDIF. */
+ if (chip->recsrc & BIT(4))
+ return 1;
+ if ((chip->recsrc & BIT(17)) &&
+ test_bit(F_HAVEDIGITAL, &chip->flags))
+ return 2;
+ return 0;
+}
+#endif
+
static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;

@@ -1001,10 +1014,73 @@ static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
return 0;
}

+#ifdef CONFIG_PM
+static int snd_msnd_card_suspend(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ struct snd_mpu401 *mpu;
+ int err;
+
+ mpu = chip->rmidi ? chip->rmidi->private_data : NULL;
+ chip->pm_recsrc = snd_msnd_pm_recsrc(chip);
+ chip->pm_mpu_input = mpu && test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
+ if (chip->pm_mpu_input)
+ snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_STOP);
+
+ err = snd_msnd_force_irq(chip, false);
+ if (err < 0) {
+ if (chip->pm_mpu_input)
+ snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START);
+ return err;
+ }
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ return 0;
+}
+
+static int snd_msnd_card_resume(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ int err;
+
+ err = snd_msnd_initialize(card);
+ if (err < 0)
+ return err;
+
+ snd_msnd_calibrate_adc(chip, chip->play_sample_rate);
+ snd_msndmix_force_recsrc(chip, chip->pm_recsrc);
+
+ err = snd_msnd_force_irq(chip, true);
+ if (err < 0)
+ return err;
+
+ if (chip->pm_mpu_input)
+ snd_msnd_send_dsp_cmd(chip, HDEX_MIDI_IN_START);
+
+ chip->nresets = 0;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+
+static int snd_msnd_isa_suspend(struct device *dev, unsigned int idx,
+ pm_message_t state)
+{
+ return snd_msnd_card_suspend(dev_get_drvdata(dev));
+}
+
+static int snd_msnd_isa_resume(struct device *dev, unsigned int idx)
+{
+ return snd_msnd_card_resume(dev_get_drvdata(dev));
+}
+#endif
+
static struct isa_driver snd_msnd_driver = {
.match = snd_msnd_isa_match,
.probe = snd_msnd_isa_probe,
- /* FIXME: suspend, resume */
+#ifdef CONFIG_PM
+ .suspend = snd_msnd_isa_suspend,
+ .resume = snd_msnd_isa_resume,
+#endif
.driver = {
.name = DEV_NAME
},
@@ -1111,6 +1187,18 @@ static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
return 0;
}

+#ifdef CONFIG_PM
+static int snd_msnd_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
+{
+ return snd_msnd_card_suspend(pnp_get_card_drvdata(pcard));
+}
+
+static int snd_msnd_pnp_resume(struct pnp_card_link *pcard)
+{
+ return snd_msnd_card_resume(pnp_get_card_drvdata(pcard));
+}
+#endif
+
static int isa_registered;
static int pnp_registered;

@@ -1127,6 +1215,10 @@ static struct pnp_card_driver msnd_pnpc_driver = {
.name = "msnd_pinnacle",
.id_table = msnd_pnpids,
.probe = snd_msnd_pnp_detect,
+#ifdef CONFIG_PM
+ .suspend = snd_msnd_pnp_suspend,
+ .resume = snd_msnd_pnp_resume,
+#endif
};
#endif /* CONFIG_PNP */

@@ -1161,4 +1253,3 @@ static void __exit snd_msnd_exit(void)

module_init(snd_msnd_init);
module_exit(snd_msnd_exit);
-

--
2.53.0