ALSA: Echoaudio - Add suspend support #2
[safe/jmp/linux-2.6] / sound / pci / echoaudio / echoaudio.c
index 79dde95..2783ce6 100644 (file)
@@ -753,6 +753,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 
        spin_lock(&chip->lock);
        switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+               DE_ACT(("pcm_trigger resume\n"));
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                DE_ACT(("pcm_trigger start\n"));
@@ -776,6 +778,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                err = start_transport(chip, channelmask,
                                      chip->pipe_cyclic_mask);
                break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               DE_ACT(("pcm_trigger suspend\n"));
        case SNDRV_PCM_TRIGGER_STOP:
                DE_ACT(("pcm_trigger stop\n"));
                for (i = 0; i < DSP_MAXPIPES; i++) {
@@ -1951,18 +1955,27 @@ static __devinit int snd_echo_create(struct snd_card *card,
                return err;
        pci_set_master(pci);
 
-       /* allocate a chip-specific data */
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (!chip) {
-               pci_disable_device(pci);
-               return -ENOMEM;
+       /* Allocate chip if needed */
+       if (!*rchip) {
+               chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+               if (!chip) {
+                       pci_disable_device(pci);
+                       return -ENOMEM;
+               }
+               DE_INIT(("chip=%p\n", chip));
+               spin_lock_init(&chip->lock);
+               chip->card = card;
+               chip->pci = pci;
+               chip->irq = -1;
+               atomic_set(&chip->opencount, 0);
+               mutex_init(&chip->mode_mutex);
+               chip->can_set_rate = 1;
+       } else {
+               /* If this was called from the resume function, chip is
+                * already allocated and it contains current card settings.
+                */
+               chip = *rchip;
        }
-       DE_INIT(("chip=%p\n", chip));
-
-       spin_lock_init(&chip->lock);
-       chip->card = card;
-       chip->pci = pci;
-       chip->irq = -1;
 
        /* PCI resource allocation */
        chip->dsp_registers_phys = pci_resource_start(pci, 0);
@@ -2002,7 +2015,9 @@ static __devinit int snd_echo_create(struct snd_card *card,
        chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
 
        err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
-       if (err) {
+       if (err >= 0)
+               err = set_mixer_defaults(chip);
+       if (err < 0) {
                DE_INIT(("init_hw err=%d\n", err));
                snd_echo_free(chip);
                return err;
@@ -2013,9 +2028,6 @@ static __devinit int snd_echo_create(struct snd_card *card,
                snd_echo_free(chip);
                return err;
        }
-       atomic_set(&chip->opencount, 0);
-       mutex_init(&chip->mode_mutex);
-       chip->can_set_rate = 1;
        *rchip = chip;
        /* Init done ! */
        return 0;
@@ -2048,6 +2060,7 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
 
        snd_card_set_dev(card, &pci->dev);
 
+       chip = NULL;    /* Tells snd_echo_create to allocate chip */
        if ((err = snd_echo_create(card, pci, &chip)) < 0) {
                snd_card_free(card);
                return err;
@@ -2187,6 +2200,112 @@ ctl_error:
 
 
 
+#if defined(CONFIG_PM)
+
+static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct echoaudio *chip = pci_get_drvdata(pci);
+
+       DE_INIT(("suspend start\n"));
+       snd_pcm_suspend_all(chip->analog_pcm);
+       snd_pcm_suspend_all(chip->digital_pcm);
+
+#ifdef ECHOCARD_HAS_MIDI
+       /* This call can sleep */
+       if (chip->midi_out)
+               snd_echo_midi_output_trigger(chip->midi_out, 0);
+#endif
+       spin_lock_irq(&chip->lock);
+       if (wait_handshake(chip)) {
+               spin_unlock_irq(&chip->lock);
+               return -EIO;
+       }
+       clear_handshake(chip);
+       if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) {
+               spin_unlock_irq(&chip->lock);
+               return -EIO;
+       }
+       spin_unlock_irq(&chip->lock);
+
+       chip->dsp_code = NULL;
+       free_irq(chip->irq, chip);
+       chip->irq = -1;
+       pci_save_state(pci);
+       pci_disable_device(pci);
+
+       DE_INIT(("suspend done\n"));
+       return 0;
+}
+
+
+
+static int snd_echo_resume(struct pci_dev *pci)
+{
+       struct echoaudio *chip = pci_get_drvdata(pci);
+       struct comm_page *commpage, *commpage_bak;
+       u32 pipe_alloc_mask;
+       int err;
+
+       DE_INIT(("resume start\n"));
+       pci_restore_state(pci);
+       commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+       commpage = chip->comm_page;
+       memcpy(commpage_bak, commpage, sizeof(struct comm_page));
+
+       err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
+       if (err < 0) {
+               kfree(commpage_bak);
+               DE_INIT(("resume init_hw err=%d\n", err));
+               snd_echo_free(chip);
+               return err;
+       }
+       DE_INIT(("resume init OK\n"));
+
+       /* Temporarily set chip->pipe_alloc_mask=0 otherwise
+        * restore_dsp_settings() fails.
+        */
+       pipe_alloc_mask = chip->pipe_alloc_mask;
+       chip->pipe_alloc_mask = 0;
+       err = restore_dsp_rettings(chip);
+       chip->pipe_alloc_mask = pipe_alloc_mask;
+       if (err < 0) {
+               kfree(commpage_bak);
+               return err;
+       }
+       DE_INIT(("resume restore OK\n"));
+
+       memcpy(&commpage->audio_format, &commpage_bak->audio_format,
+               sizeof(commpage->audio_format));
+       memcpy(&commpage->sglist_addr, &commpage_bak->sglist_addr,
+               sizeof(commpage->sglist_addr));
+       memcpy(&commpage->midi_output, &commpage_bak->midi_output,
+               sizeof(commpage->midi_output));
+       kfree(commpage_bak);
+
+       if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
+                       ECHOCARD_NAME, chip)) {
+               snd_echo_free(chip);
+               snd_printk(KERN_ERR "cannot grab irq\n");
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+       DE_INIT(("resume irq=%d\n", chip->irq));
+
+#ifdef ECHOCARD_HAS_MIDI
+       if (chip->midi_input_enabled)
+               enable_midi_input(chip, TRUE);
+       if (chip->midi_out)
+               snd_echo_midi_output_trigger(chip->midi_out, 1);
+#endif
+
+       DE_INIT(("resume done\n"));
+       return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+
 static void __devexit snd_echo_remove(struct pci_dev *pci)
 {
        struct echoaudio *chip;
@@ -2209,6 +2328,10 @@ static struct pci_driver driver = {
        .id_table = snd_echo_ids,
        .probe = snd_echo_probe,
        .remove = __devexit_p(snd_echo_remove),
+#ifdef CONFIG_PM
+       .suspend = snd_echo_suspend,
+       .resume = snd_echo_resume,
+#endif /* CONFIG_PM */
 };