summaryrefslogtreecommitdiff
path: root/sound/isa/wavefront
diff options
context:
space:
mode:
authorCássio Gabriel <cassiogabrielcontato@gmail.com>2026-04-27 12:36:49 -0300
committerTakashi Iwai <tiwai@suse.de>2026-04-29 07:52:22 +0200
commited060707d4bac8f1a2fcf83af4e1f01d3337893c (patch)
tree543c388935973972d32d514bd353c08ad93da3eb /sound/isa/wavefront
parenta895279d060d3b16a653bd434f8562eadd37baab (diff)
downloadlinux-next-ed060707d4bac8f1a2fcf83af4e1f01d3337893c.tar.gz
linux-next-ed060707d4bac8f1a2fcf83af4e1f01d3337893c.zip
ALSA: wavefront: add suspend and resume support
The WaveFront driver still lacks support for suspend and resume in both the ISA and PnP driver tables. Wire the driver into ALSA PM by storing the WSS codec pointer in the card private data and adding shared suspend/resume callbacks. Resume cannot simply rerun snd_wavefront_start(), because with the default fx_raw=1 setting that would reset the synth on every resume and discard uploaded WaveFront RAM contents. Cache wavefront.os for PM, probe the ICS2115 after resume and only run the full reset/bootstrap path when the board comes back raw. When the firmware is still running, refresh the software slot bookkeeping and restore the MIDI routing state without forcing a synth reset. Also quiesce and restart the WaveFront MIDI output timer across suspend and resume so active rawmidi output does not race the PM transition. This restores the card to a usable baseline after resume while preserving uploaded samples and programs when the hardware state survives suspend. If the board resumes raw, userspace still needs to reload custom synth contents. Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com> Link: https://patch.msgid.link/20260427-wavefront-pm-v1-1-9c1b6a898673@gmail.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/isa/wavefront')
-rw-r--r--sound/isa/wavefront/wavefront.c61
-rw-r--r--sound/isa/wavefront/wavefront_midi.c47
-rw-r--r--sound/isa/wavefront/wavefront_synth.c96
3 files changed, 201 insertions, 3 deletions
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 07c68568091d..f4a0c21c905e 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -353,6 +353,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
dev_err(card->dev, "can't allocate WSS device\n");
return err;
}
+ acard->chip = chip;
err = snd_wss_pcm(chip, 0);
if (err < 0)
@@ -400,6 +401,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
acard->wavefront.irq = ics2115_irq[dev];
card->sync_irq = acard->wavefront.irq;
acard->wavefront.base = ics2115_port[dev];
+ snd_wavefront_cache_firmware(&acard->wavefront);
wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard);
if (wavefront_synth == NULL) {
@@ -553,12 +555,51 @@ static int snd_wavefront_isa_probe(struct device *pdev,
return 0;
}
+#ifdef CONFIG_PM
+static int snd_wavefront_suspend(struct snd_card *card)
+{
+ snd_wavefront_card_t *acard = card->private_data;
+
+ snd_wavefront_midi_suspend(acard);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ acard->chip->suspend(acard->chip);
+ return 0;
+}
+
+static int snd_wavefront_resume(struct snd_card *card)
+{
+ snd_wavefront_card_t *acard = card->private_data;
+ int err;
+
+ acard->chip->resume(acard->chip);
+ err = snd_wavefront_resume_synth(acard);
+ if (err < 0)
+ return err;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+
+static int snd_wavefront_isa_suspend(struct device *dev, unsigned int id,
+ pm_message_t state)
+{
+ return snd_wavefront_suspend(dev_get_drvdata(dev));
+}
+
+static int snd_wavefront_isa_resume(struct device *dev, unsigned int id)
+{
+ return snd_wavefront_resume(dev_get_drvdata(dev));
+}
+#endif
+
#define DEV_NAME "wavefront"
static struct isa_driver snd_wavefront_driver = {
.match = snd_wavefront_isa_match,
.probe = snd_wavefront_isa_probe,
- /* FIXME: suspend, resume */
+#ifdef CONFIG_PM
+ .suspend = snd_wavefront_isa_suspend,
+ .resume = snd_wavefront_isa_resume,
+#endif
.driver = {
.name = DEV_NAME
},
@@ -600,12 +641,28 @@ static int snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
+#ifdef CONFIG_PM
+static int snd_wavefront_pnpc_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
+{
+ return snd_wavefront_suspend(pnp_get_card_drvdata(pcard));
+}
+
+static int snd_wavefront_pnpc_resume(struct pnp_card_link *pcard)
+{
+ return snd_wavefront_resume(pnp_get_card_drvdata(pcard));
+}
+#endif
+
static struct pnp_card_driver wavefront_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "wavefront",
.id_table = snd_wavefront_pnpids,
.probe = snd_wavefront_pnp_detect,
- /* FIXME: suspend,resume */
+#ifdef CONFIG_PM
+ .suspend = snd_wavefront_pnpc_suspend,
+ .resume = snd_wavefront_pnpc_resume,
+#endif
};
#endif /* CONFIG_PNP */
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index 69d87c4cafae..fb184d9ef284 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -455,6 +455,49 @@ snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
card->wavefront.midi.isvirtual = 0;
}
+void
+snd_wavefront_midi_suspend(snd_wavefront_card_t *card)
+
+{
+ snd_wavefront_midi_t *midi = &card->wavefront.midi;
+
+ if (!midi->istimer)
+ return;
+
+ timer_delete_sync(&midi->timer);
+
+ guard(spinlock_irqsave)(&midi->virtual);
+ midi->istimer = 0;
+}
+
+void
+snd_wavefront_midi_resume(snd_wavefront_card_t *card)
+
+{
+ snd_wavefront_midi_t *midi = &card->wavefront.midi;
+ int istimer = 0;
+ bool pending_output = false;
+
+ midi->timer_card = card;
+
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ if (midi->mode[internal_mpu] & MPU401_MODE_OUTPUT_TRIGGER)
+ istimer++;
+ if (midi->mode[external_mpu] & MPU401_MODE_OUTPUT_TRIGGER)
+ istimer++;
+ if (!istimer)
+ return;
+
+ midi->istimer = istimer;
+ timer_setup(&midi->timer, snd_wavefront_midi_output_timer, 0);
+ mod_timer(&midi->timer, 1 + jiffies);
+ pending_output = true;
+ }
+
+ if (pending_output)
+ snd_wavefront_midi_output_write(card);
+}
+
int
snd_wavefront_midi_start (snd_wavefront_card_t *card)
@@ -466,6 +509,7 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
dev = &card->wavefront;
midi = &dev->midi;
+ midi->timer_card = card;
/* The ICS2115 MPU-401 interface doesn't do anything
until its set into UART mode.
@@ -511,6 +555,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
dev_warn(card->wavefront.card->dev,
"can't enable MIDI-IN-2-synth routing.\n");
/* XXX error ? */
+ } else {
+ dev->midi_in_to_synth = 1;
}
/* Turn on Virtual MIDI, but first *always* turn it off,
@@ -553,4 +599,3 @@ const struct snd_rawmidi_ops snd_wavefront_midi_input =
.close = snd_wavefront_midi_input_close,
.trigger = snd_wavefront_midi_input_trigger,
};
-
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 33b563707a58..2f57a6795d22 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1626,6 +1626,14 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
"support for sample aliases still being considered.\n");
break;
+ case WFC_MISYNTH_OFF:
+ dev->midi_in_to_synth = 0;
+ break;
+
+ case WFC_MISYNTH_ON:
+ dev->midi_in_to_synth = 1;
+ break;
+
case WFC_VMIDI_OFF:
snd_wavefront_midi_disable_virtual (acard);
break;
@@ -1639,6 +1647,83 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
return 0;
}
+static int
+wavefront_restore_midi_state(snd_wavefront_card_t *acard, char isvirtual,
+ char midi_in_to_synth)
+{
+ snd_wavefront_t *dev = &acard->wavefront;
+ unsigned char rbuf[4], wbuf[4];
+
+ if (dev->midi_in_to_synth != midi_in_to_synth) {
+ if (snd_wavefront_cmd(dev, midi_in_to_synth ?
+ WFC_MISYNTH_ON : WFC_MISYNTH_OFF,
+ rbuf, wbuf)) {
+ dev_err(dev->card->dev,
+ "cannot restore MIDI-IN routing after resume\n");
+ return -EIO;
+ }
+ dev->midi_in_to_synth = midi_in_to_synth;
+ }
+
+ if (dev->midi.isvirtual != isvirtual) {
+ if (snd_wavefront_cmd(dev, isvirtual ?
+ WFC_VMIDI_ON : WFC_VMIDI_OFF,
+ rbuf, wbuf)) {
+ dev_err(dev->card->dev,
+ "cannot restore virtual MIDI mode after resume\n");
+ return -EIO;
+ }
+ if (isvirtual)
+ snd_wavefront_midi_enable_virtual(acard);
+ else
+ snd_wavefront_midi_disable_virtual(acard);
+ }
+
+ return 0;
+}
+
+int snd_wavefront_resume_synth(snd_wavefront_card_t *acard)
+{
+ snd_wavefront_t *dev = &acard->wavefront;
+ char was_virtual = dev->midi.isvirtual;
+ char midi_in_to_synth = dev->midi_in_to_synth;
+ char rom_samples_rdonly = dev->rom_samples_rdonly;
+ int err;
+
+ err = snd_wavefront_detect(acard);
+ if (err < 0)
+ dev->israw = 1;
+
+ if (dev->israw) {
+ dev->fx_initialized = 0;
+ err = snd_wavefront_start(dev);
+ if (err < 0)
+ return err;
+ } else {
+ dev->has_fx = (snd_wavefront_fx_detect(dev) == 0);
+ wavefront_get_sample_status(dev, 0);
+ wavefront_get_program_status(dev);
+ wavefront_get_patch_status(dev);
+ outb(0x80 | 0x40 | 0x20, dev->control_port);
+ }
+
+ dev->rom_samples_rdonly = rom_samples_rdonly;
+ dev->midi.base = dev->base;
+
+ err = snd_wavefront_midi_start(acard);
+ if (err < 0)
+ return err;
+
+ err = wavefront_restore_midi_state(acard, was_virtual,
+ midi_in_to_synth);
+ if (err < 0)
+ return err;
+
+ snd_wavefront_midi_resume(acard);
+
+ return 0;
+}
+
int
snd_wavefront_synth_open (struct snd_hwdep *hw, struct file *file)
@@ -2032,6 +2117,17 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
return 1;
}
+void snd_wavefront_cache_firmware(snd_wavefront_t *dev)
+{
+ int err;
+
+ err = firmware_request_cache(dev->card->dev, ospath);
+ if (err < 0)
+ dev_warn(dev->card->dev,
+ "unable to cache firmware %s for resume: %d\n",
+ ospath, err);
+}
+
static int
wavefront_do_reset (snd_wavefront_t *dev)