diff options
Diffstat (limited to 'sound/pci/oxygen/oxygen_lib.c')
-rw-r--r-- | sound/pci/oxygen/oxygen_lib.c | 106 |
1 files changed, 103 insertions, 3 deletions
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 897697d43506..22f37851045e 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c | |||
@@ -32,7 +32,7 @@ | |||
32 | 32 | ||
33 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | 33 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); |
34 | MODULE_DESCRIPTION("C-Media CMI8788 helper library"); | 34 | MODULE_DESCRIPTION("C-Media CMI8788 helper library"); |
35 | MODULE_LICENSE("GPL"); | 35 | MODULE_LICENSE("GPL v2"); |
36 | 36 | ||
37 | 37 | ||
38 | static irqreturn_t oxygen_interrupt(int dummy, void *dev_id) | 38 | static irqreturn_t oxygen_interrupt(int dummy, void *dev_id) |
@@ -173,7 +173,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry, | |||
173 | int i, j; | 173 | int i, j; |
174 | 174 | ||
175 | snd_iprintf(buffer, "CMI8788\n\n"); | 175 | snd_iprintf(buffer, "CMI8788\n\n"); |
176 | for (i = 0; i < 0x100; i += 0x10) { | 176 | for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) { |
177 | snd_iprintf(buffer, "%02x:", i); | 177 | snd_iprintf(buffer, "%02x:", i); |
178 | for (j = 0; j < 0x10; ++j) | 178 | for (j = 0; j < 0x10; ++j) |
179 | snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j)); | 179 | snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j)); |
@@ -314,6 +314,10 @@ static void oxygen_init(struct oxygen *chip) | |||
314 | OXYGEN_SPDIF_LOCK_MASK | | 314 | OXYGEN_SPDIF_LOCK_MASK | |
315 | OXYGEN_SPDIF_RATE_MASK); | 315 | OXYGEN_SPDIF_RATE_MASK); |
316 | oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits); | 316 | oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits); |
317 | oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, | ||
318 | OXYGEN_2WIRE_LENGTH_8 | | ||
319 | OXYGEN_2WIRE_INTERRUPT_MASK | | ||
320 | OXYGEN_2WIRE_SPEED_STANDARD); | ||
317 | oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK); | 321 | oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK); |
318 | oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0); | 322 | oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0); |
319 | oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0); | 323 | oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0); |
@@ -455,7 +459,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, | |||
455 | } | 459 | } |
456 | 460 | ||
457 | if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) || | 461 | if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) || |
458 | pci_resource_len(pci, 0) < 0x100) { | 462 | pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) { |
459 | snd_printk(KERN_ERR "invalid PCI I/O range\n"); | 463 | snd_printk(KERN_ERR "invalid PCI I/O range\n"); |
460 | err = -ENXIO; | 464 | err = -ENXIO; |
461 | goto err_pci_regions; | 465 | goto err_pci_regions; |
@@ -534,3 +538,99 @@ void oxygen_pci_remove(struct pci_dev *pci) | |||
534 | pci_set_drvdata(pci, NULL); | 538 | pci_set_drvdata(pci, NULL); |
535 | } | 539 | } |
536 | EXPORT_SYMBOL(oxygen_pci_remove); | 540 | EXPORT_SYMBOL(oxygen_pci_remove); |
541 | |||
542 | #ifdef CONFIG_PM | ||
543 | int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state) | ||
544 | { | ||
545 | struct snd_card *card = pci_get_drvdata(pci); | ||
546 | struct oxygen *chip = card->private_data; | ||
547 | unsigned int i, saved_interrupt_mask; | ||
548 | |||
549 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
550 | |||
551 | for (i = 0; i < PCM_COUNT; ++i) | ||
552 | if (chip->streams[i]) | ||
553 | snd_pcm_suspend(chip->streams[i]); | ||
554 | |||
555 | if (chip->model->suspend) | ||
556 | chip->model->suspend(chip); | ||
557 | |||
558 | spin_lock_irq(&chip->reg_lock); | ||
559 | saved_interrupt_mask = chip->interrupt_mask; | ||
560 | chip->interrupt_mask = 0; | ||
561 | oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); | ||
562 | oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); | ||
563 | spin_unlock_irq(&chip->reg_lock); | ||
564 | |||
565 | synchronize_irq(chip->irq); | ||
566 | flush_scheduled_work(); | ||
567 | chip->interrupt_mask = saved_interrupt_mask; | ||
568 | |||
569 | pci_disable_device(pci); | ||
570 | pci_save_state(pci); | ||
571 | pci_set_power_state(pci, pci_choose_state(pci, state)); | ||
572 | return 0; | ||
573 | } | ||
574 | EXPORT_SYMBOL(oxygen_pci_suspend); | ||
575 | |||
576 | static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = { | ||
577 | 0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff, | ||
578 | 0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000 | ||
579 | }; | ||
580 | static const u32 ac97_registers_to_restore[2][0x40 / 32] = { | ||
581 | { 0x18284fa2, 0x03060000 }, | ||
582 | { 0x00007fa6, 0x00200000 } | ||
583 | }; | ||
584 | |||
585 | static inline int is_bit_set(const u32 *bitmap, unsigned int bit) | ||
586 | { | ||
587 | return bitmap[bit / 32] & (1 << (bit & 31)); | ||
588 | } | ||
589 | |||
590 | static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec) | ||
591 | { | ||
592 | unsigned int i; | ||
593 | |||
594 | oxygen_write_ac97(chip, codec, AC97_RESET, 0); | ||
595 | msleep(1); | ||
596 | for (i = 1; i < 0x40; ++i) | ||
597 | if (is_bit_set(ac97_registers_to_restore[codec], i)) | ||
598 | oxygen_write_ac97(chip, codec, i * 2, | ||
599 | chip->saved_ac97_registers[codec][i]); | ||
600 | } | ||
601 | |||
602 | int oxygen_pci_resume(struct pci_dev *pci) | ||
603 | { | ||
604 | struct snd_card *card = pci_get_drvdata(pci); | ||
605 | struct oxygen *chip = card->private_data; | ||
606 | unsigned int i; | ||
607 | |||
608 | pci_set_power_state(pci, PCI_D0); | ||
609 | pci_restore_state(pci); | ||
610 | if (pci_enable_device(pci) < 0) { | ||
611 | snd_printk(KERN_ERR "cannot reenable device"); | ||
612 | snd_card_disconnect(card); | ||
613 | return -EIO; | ||
614 | } | ||
615 | pci_set_master(pci); | ||
616 | |||
617 | oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); | ||
618 | oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); | ||
619 | for (i = 0; i < OXYGEN_IO_SIZE; ++i) | ||
620 | if (is_bit_set(registers_to_restore, i)) | ||
621 | oxygen_write8(chip, i, chip->saved_registers._8[i]); | ||
622 | if (chip->has_ac97_0) | ||
623 | oxygen_restore_ac97(chip, 0); | ||
624 | if (chip->has_ac97_1) | ||
625 | oxygen_restore_ac97(chip, 1); | ||
626 | |||
627 | if (chip->model->resume) | ||
628 | chip->model->resume(chip); | ||
629 | |||
630 | oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); | ||
631 | |||
632 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
633 | return 0; | ||
634 | } | ||
635 | EXPORT_SYMBOL(oxygen_pci_resume); | ||
636 | #endif /* CONFIG_PM */ | ||