aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/echoaudio/echoaudio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/echoaudio/echoaudio.c')
-rw-r--r--sound/pci/echoaudio/echoaudio.c153
1 files changed, 138 insertions, 15 deletions
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 79dde9592847..2783ce6c236e 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -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 */
};