* [PATCH 0/2] ALSA: msnd: add suspend/resume support
@ 2026-04-09 5:07 Cássio Gabriel
2026-04-09 5:07 ` [PATCH 1/2] ALSA: msnd: prepare system sleep support Cássio Gabriel
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Cássio Gabriel @ 2026-04-09 5:07 UTC (permalink / raw)
To: Takashi Iwai, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel
The msnd drivers still have no system-sleep callbacks, and the shared
driver file still carries a FIXME for suspend/resume. Probe already
depends on rebuilding volatile device state by resetting the DSP,
uploading firmware, reinitializing shared memory, restoring mixer
settings, and calibrating the ADC. Without matching PM callbacks, the
driver has no defined way to recover that state after suspend.
This small series adds the missing suspend/resume support with the
minimum scope needed for the current driver.
Patch 1 prepares the driver for PM by:
- accepting SNDRV_PCM_TRIGGER_SUSPEND in the PCM trigger callbacks
- factoring the IRQ enable/disable bodies and adding a helper that can
quiesce or rearm the shared IRQ without disturbing irq_ref
- restoring cached master-volume values when mixer state is rebuilt
after DSP reinitialization
Patch 2 adds the actual PM callbacks by:
- saving the current capture-source selection and msnd-side MIDI input
state
- adding shared card suspend/resume helpers
- wiring those helpers into the ISA driver and the Pinnacle PnP card
driver with the current callback signatures
The series intentionally does not add trigger-level PCM resume support.
The driver can rebuild hardware state on system resume, but it does not
preserve enough runtime queue state to justify advertising full
SNDRV_PCM_INFO_RESUME semantics in this change.
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
Cássio Gabriel (2):
ALSA: msnd: prepare system sleep support
ALSA: msnd: add ISA and PnP system sleep callbacks
sound/isa/msnd/msnd.c | 72 ++++++++++++++++++---------
sound/isa/msnd/msnd.h | 3 ++
sound/isa/msnd/msnd_pinnacle.c | 95 +++++++++++++++++++++++++++++++++++-
sound/isa/msnd/msnd_pinnacle_mixer.c | 4 ++
4 files changed, 149 insertions(+), 25 deletions(-)
---
base-commit: 9f2a23bd5d30dc0968e56a2b6e5edb58aff1bc6d
change-id: 20260403-msnd-pm-support-e14d428ca426
Best regards,
--
Cássio Gabriel <cassiogabrielcontato@gmail.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] ALSA: msnd: prepare system sleep support
2026-04-09 5:07 [PATCH 0/2] ALSA: msnd: add suspend/resume support Cássio Gabriel
@ 2026-04-09 5:07 ` Cássio Gabriel
2026-04-09 5:07 ` [PATCH 2/2] ALSA: msnd: add ISA and PnP system sleep callbacks Cássio Gabriel
2026-04-09 10:02 ` [PATCH 0/2] ALSA: msnd: add suspend/resume support Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Cássio Gabriel @ 2026-04-09 5:07 UTC (permalink / raw)
To: Takashi Iwai, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel
System suspend cannot work for msnd today because the PCM trigger
paths reject SNDRV_PCM_TRIGGER_SUSPEND, and the driver has only
refcounted IRQ helpers.
Add the small helpers needed by the PM callbacks and restore master
volume from the cached ALSA mixer state when the DSP is
reinitialized.
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
sound/isa/msnd/msnd.c | 72 ++++++++++++++++++++++++------------
sound/isa/msnd/msnd.h | 1 +
sound/isa/msnd/msnd_pinnacle_mixer.c | 4 ++
3 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c
index 5e350234d572..77367e102fda 100644
--- a/sound/isa/msnd/msnd.c
+++ b/sound/isa/msnd/msnd.c
@@ -127,11 +127,8 @@ int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
}
EXPORT_SYMBOL(snd_msnd_upload_host);
-int snd_msnd_enable_irq(struct snd_msnd *dev)
+static int __snd_msnd_enable_irq(struct snd_msnd *dev)
{
- if (dev->irq_ref++)
- return 0;
-
dev_dbg(dev->card->dev, LOGNAME ": Enabling IRQ\n");
guard(spinlock_irqsave)(&dev->lock);
@@ -152,17 +149,9 @@ int snd_msnd_enable_irq(struct snd_msnd *dev)
return -EIO;
}
-EXPORT_SYMBOL(snd_msnd_enable_irq);
-int snd_msnd_disable_irq(struct snd_msnd *dev)
+static int __snd_msnd_disable_irq(struct snd_msnd *dev)
{
- if (--dev->irq_ref > 0)
- return 0;
-
- if (dev->irq_ref < 0)
- dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n",
- dev->irq_ref);
-
dev_dbg(dev->card->dev, LOGNAME ": Disabling IRQ\n");
guard(spinlock_irqsave)(&dev->lock);
@@ -178,8 +167,39 @@ int snd_msnd_disable_irq(struct snd_msnd *dev)
return -EIO;
}
+
+int snd_msnd_enable_irq(struct snd_msnd *dev)
+{
+ if (dev->irq_ref++)
+ return 0;
+
+ return __snd_msnd_enable_irq(dev);
+}
+EXPORT_SYMBOL(snd_msnd_enable_irq);
+
+int snd_msnd_disable_irq(struct snd_msnd *dev)
+{
+ if (--dev->irq_ref > 0)
+ return 0;
+
+ if (dev->irq_ref < 0)
+ dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n",
+ dev->irq_ref);
+
+ return __snd_msnd_disable_irq(dev);
+}
EXPORT_SYMBOL(snd_msnd_disable_irq);
+int snd_msnd_force_irq(struct snd_msnd *dev, bool enable)
+{
+ if (!dev->irq_ref)
+ return 0;
+
+ return enable ? __snd_msnd_enable_irq(dev) :
+ __snd_msnd_disable_irq(dev);
+}
+EXPORT_SYMBOL(snd_msnd_force_irq);
+
static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
{
long tmp = (size * HZ * chip->play_sample_size) / 8;
@@ -507,25 +527,27 @@ static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
- int result = 0;
- if (cmd == SNDRV_PCM_TRIGGER_START) {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
dev_dbg(chip->card->dev, "%s(START)\n", __func__);
chip->banksPlayed = 0;
set_bit(F_WRITING, &chip->flags);
snd_msnd_DAPQ(chip, 1);
- } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
dev_dbg(chip->card->dev, "%s(STOP)\n", __func__);
- /* interrupt diagnostic, comment this out later */
clear_bit(F_WRITING, &chip->flags);
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
- } else {
+ break;
+ default:
dev_dbg(chip->card->dev, "%s(?????)\n", __func__);
- result = -EINVAL;
+ return -EINVAL;
}
dev_dbg(chip->card->dev, "%s() ENDE\n", __func__);
- return result;
+ return 0;
}
static snd_pcm_uframes_t
@@ -589,17 +611,22 @@ static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
{
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
- if (cmd == SNDRV_PCM_TRIGGER_START) {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
chip->last_recbank = -1;
set_bit(F_READING, &chip->flags);
if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
return 0;
clear_bit(F_READING, &chip->flags);
- } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
clear_bit(F_READING, &chip->flags);
snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
return 0;
+ default:
+ break;
}
return -EINVAL;
}
@@ -668,4 +695,3 @@ EXPORT_SYMBOL(snd_msnd_pcm);
MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
MODULE_LICENSE("GPL");
-
diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
index 3d7810ed9186..b25beca25c0d 100644
--- a/sound/isa/msnd/msnd.h
+++ b/sound/isa/msnd/msnd.h
@@ -280,6 +280,7 @@ int snd_msnd_upload_host(struct snd_msnd *chip,
const u8 *bin, int len);
int snd_msnd_enable_irq(struct snd_msnd *chip);
int snd_msnd_disable_irq(struct snd_msnd *chip);
+int snd_msnd_force_irq(struct snd_msnd *chip, bool enable);
void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file);
int snd_msnd_DAPQ(struct snd_msnd *chip, int start);
int snd_msnd_DARQ(struct snd_msnd *chip, int start);
diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c
index ec354483b9f8..8ca987221753 100644
--- a/sound/isa/msnd/msnd_pinnacle_mixer.c
+++ b/sound/isa/msnd/msnd_pinnacle_mixer.c
@@ -310,6 +310,10 @@ EXPORT_SYMBOL(snd_msndmix_new);
void snd_msndmix_setup(struct snd_msnd *dev)
{
+ writew(dev->left_levels[MSND_MIXER_VOLUME],
+ dev->SMA + SMA_wCurrMastVolLeft);
+ writew(dev->right_levels[MSND_MIXER_VOLUME],
+ dev->SMA + SMA_wCurrMastVolRight);
update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
update_volm(MSND_MIXER_PCM, wCurrPlayVol);
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] ALSA: msnd: add ISA and PnP system sleep callbacks
2026-04-09 5:07 [PATCH 0/2] ALSA: msnd: add suspend/resume support Cássio Gabriel
2026-04-09 5:07 ` [PATCH 1/2] ALSA: msnd: prepare system sleep support Cássio Gabriel
@ 2026-04-09 5:07 ` Cássio Gabriel
2026-04-09 10:02 ` [PATCH 0/2] ALSA: msnd: add suspend/resume support Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Cássio Gabriel @ 2026-04-09 5:07 UTC (permalink / raw)
To: Takashi Iwai, Jaroslav Kysela
Cc: linux-sound, linux-kernel, Cássio Gabriel
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@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] ALSA: msnd: add suspend/resume support
2026-04-09 5:07 [PATCH 0/2] ALSA: msnd: add suspend/resume support Cássio Gabriel
2026-04-09 5:07 ` [PATCH 1/2] ALSA: msnd: prepare system sleep support Cássio Gabriel
2026-04-09 5:07 ` [PATCH 2/2] ALSA: msnd: add ISA and PnP system sleep callbacks Cássio Gabriel
@ 2026-04-09 10:02 ` Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-04-09 10:02 UTC (permalink / raw)
To: Cássio Gabriel
Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel
On Thu, 09 Apr 2026 07:07:44 +0200,
Cássio Gabriel wrote:
>
> The msnd drivers still have no system-sleep callbacks, and the shared
> driver file still carries a FIXME for suspend/resume. Probe already
> depends on rebuilding volatile device state by resetting the DSP,
> uploading firmware, reinitializing shared memory, restoring mixer
> settings, and calibrating the ADC. Without matching PM callbacks, the
> driver has no defined way to recover that state after suspend.
>
> This small series adds the missing suspend/resume support with the
> minimum scope needed for the current driver.
>
> Patch 1 prepares the driver for PM by:
> - accepting SNDRV_PCM_TRIGGER_SUSPEND in the PCM trigger callbacks
> - factoring the IRQ enable/disable bodies and adding a helper that can
> quiesce or rearm the shared IRQ without disturbing irq_ref
> - restoring cached master-volume values when mixer state is rebuilt
> after DSP reinitialization
>
> Patch 2 adds the actual PM callbacks by:
> - saving the current capture-source selection and msnd-side MIDI input
> state
> - adding shared card suspend/resume helpers
> - wiring those helpers into the ISA driver and the Pinnacle PnP card
> driver with the current callback signatures
>
> The series intentionally does not add trigger-level PCM resume support.
> The driver can rebuild hardware state on system resume, but it does not
> preserve enough runtime queue state to justify advertising full
> SNDRV_PCM_INFO_RESUME semantics in this change.
>
> Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
> ---
> Cássio Gabriel (2):
> ALSA: msnd: prepare system sleep support
> ALSA: msnd: add ISA and PnP system sleep callbacks
Applied both patches to for-next branch. Thanks.
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-09 10:03 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-09 5:07 [PATCH 0/2] ALSA: msnd: add suspend/resume support Cássio Gabriel
2026-04-09 5:07 ` [PATCH 1/2] ALSA: msnd: prepare system sleep support Cássio Gabriel
2026-04-09 5:07 ` [PATCH 2/2] ALSA: msnd: add ISA and PnP system sleep callbacks Cássio Gabriel
2026-04-09 10:02 ` [PATCH 0/2] ALSA: msnd: add suspend/resume support Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox