diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h index 30f508a56766..ac749bb2b836 100644 --- a/include/sound/snd_wavefront.h +++ b/include/sound/snd_wavefront.h @@ -12,6 +12,7 @@ struct _snd_wavefront_midi; struct _snd_wavefront_card; struct _snd_wavefront; +struct snd_wss; typedef struct _snd_wavefront_midi snd_wavefront_midi_t; typedef struct _snd_wavefront_card snd_wavefront_card_t; @@ -46,6 +47,8 @@ extern void snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *); extern void snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *); extern void snd_wavefront_midi_interrupt (snd_wavefront_card_t *); extern int snd_wavefront_midi_start (snd_wavefront_card_t *); +void snd_wavefront_midi_suspend(snd_wavefront_card_t *card); +void snd_wavefront_midi_resume(snd_wavefront_card_t *card); struct _snd_wavefront { unsigned long irq; /* "you were one, one of the few ..." */ @@ -93,6 +96,7 @@ struct _snd_wavefront { int samples_used; /* how many */ char interrupts_are_midi; /* h/w MPU interrupts enabled ? */ char rom_samples_rdonly; /* can we write on ROM samples */ + char midi_in_to_synth; /* route external MIDI to synth */ spinlock_t irq_lock; wait_queue_head_t interrupt_sleeper; snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */ @@ -101,6 +105,7 @@ struct _snd_wavefront { struct _snd_wavefront_card { snd_wavefront_t wavefront; + struct snd_wss *chip; #ifdef CONFIG_PNP struct pnp_dev *wss; struct pnp_dev *ctrl; @@ -110,8 +115,10 @@ struct _snd_wavefront_card { }; extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card); +void snd_wavefront_cache_firmware(snd_wavefront_t *dev); extern int snd_wavefront_start (snd_wavefront_t *dev); extern int snd_wavefront_detect (snd_wavefront_card_t *card); +int snd_wavefront_resume_synth(snd_wavefront_card_t *card); extern int snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *, unsigned char *); 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)