24 |
#include <linux/delay.h> |
#include <linux/delay.h> |
25 |
#include <linux/init.h> |
#include <linux/init.h> |
26 |
#include <linux/interrupt.h> |
#include <linux/interrupt.h> |
27 |
|
#include <linux/pm.h> |
28 |
#include <linux/slab.h> |
#include <linux/slab.h> |
29 |
#include <linux/ioport.h> |
#include <linux/ioport.h> |
30 |
#include <sound/core.h> |
#include <sound/core.h> |
626 |
|
|
627 |
*/ |
*/ |
628 |
|
|
629 |
|
static void snd_ad1848_thinkpad_twiddle(ad1848_t *chip, int on) { |
630 |
|
|
631 |
|
int tmp; |
632 |
|
|
633 |
|
if (!chip->thinkpad_flag) return; |
634 |
|
|
635 |
|
outb(0x1c, AD1848_THINKPAD_CTL_PORT1); |
636 |
|
tmp = inb(AD1848_THINKPAD_CTL_PORT2); |
637 |
|
|
638 |
|
if (on) |
639 |
|
/* turn it on */ |
640 |
|
tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT; |
641 |
|
else |
642 |
|
/* turn it off */ |
643 |
|
tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT; |
644 |
|
|
645 |
|
outb(tmp, AD1848_THINKPAD_CTL_PORT2); |
646 |
|
|
647 |
|
} |
648 |
|
|
649 |
|
#ifdef CONFIG_PM |
650 |
|
static void snd_ad1848_suspend(ad1848_t *chip) { |
651 |
|
|
652 |
|
snd_card_t *card = chip->card; |
653 |
|
|
654 |
|
if (card->power_state == SNDRV_CTL_POWER_D3hot) |
655 |
|
return; |
656 |
|
|
657 |
|
snd_pcm_suspend_all(chip->pcm); |
658 |
|
/* FIXME: save registers? */ |
659 |
|
|
660 |
|
if (chip->thinkpad_flag) |
661 |
|
snd_ad1848_thinkpad_twiddle(chip, 0); |
662 |
|
|
663 |
|
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); |
664 |
|
} |
665 |
|
|
666 |
|
static void snd_ad1848_resume(ad1848_t *chip) { |
667 |
|
|
668 |
|
snd_card_t *card = chip->card; |
669 |
|
|
670 |
|
if (card->power_state == SNDRV_CTL_POWER_D0) |
671 |
|
return; |
672 |
|
|
673 |
|
if (chip->thinkpad_flag) |
674 |
|
snd_ad1848_thinkpad_twiddle(chip, 1); |
675 |
|
|
676 |
|
/* FIXME: restore registers? */ |
677 |
|
|
678 |
|
snd_power_change_state(card, SNDRV_CTL_POWER_D0); |
679 |
|
} |
680 |
|
|
681 |
|
/* callback for control API */ |
682 |
|
static int snd_ad1848_set_power_state(snd_card_t *card, unsigned int power_state) |
683 |
|
{ |
684 |
|
ad1848_t *chip = (ad1848_t *) card->power_state_private_data; |
685 |
|
switch (power_state) { |
686 |
|
case SNDRV_CTL_POWER_D0: |
687 |
|
case SNDRV_CTL_POWER_D1: |
688 |
|
case SNDRV_CTL_POWER_D2: |
689 |
|
snd_ad1848_resume(chip); |
690 |
|
break; |
691 |
|
case SNDRV_CTL_POWER_D3hot: |
692 |
|
case SNDRV_CTL_POWER_D3cold: |
693 |
|
snd_ad1848_suspend(chip); |
694 |
|
break; |
695 |
|
default: |
696 |
|
return -EINVAL; |
697 |
|
} |
698 |
|
return 0; |
699 |
|
} |
700 |
|
|
701 |
|
static int snd_ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) |
702 |
|
{ |
703 |
|
ad1848_t *chip = snd_magic_cast(ad1848_t, dev->data, return 0); |
704 |
|
|
705 |
|
switch (rqst) { |
706 |
|
case PM_SUSPEND: |
707 |
|
snd_ad1848_suspend(chip); |
708 |
|
break; |
709 |
|
case PM_RESUME: |
710 |
|
snd_ad1848_resume(chip); |
711 |
|
break; |
712 |
|
} |
713 |
|
return 0; |
714 |
|
} |
715 |
|
|
716 |
|
#endif /* CONFIG_PM */ |
717 |
|
|
718 |
static int snd_ad1848_probe(ad1848_t * chip) |
static int snd_ad1848_probe(ad1848_t * chip) |
719 |
{ |
{ |
720 |
unsigned long flags; |
unsigned long flags; |
889 |
|
|
890 |
static int snd_ad1848_free(ad1848_t *chip) |
static int snd_ad1848_free(ad1848_t *chip) |
891 |
{ |
{ |
892 |
|
#ifdef CONFIG_PM |
893 |
|
if (chip->thinkpad_pmstate) |
894 |
|
pm_unregister(chip->thinkpad_pmstate); |
895 |
|
#endif |
896 |
if (chip->res_port) { |
if (chip->res_port) { |
897 |
release_resource(chip->res_port); |
release_resource(chip->res_port); |
898 |
kfree_nocheck(chip->res_port); |
kfree_nocheck(chip->res_port); |
964 |
} |
} |
965 |
chip->dma = dma; |
chip->dma = dma; |
966 |
|
|
967 |
|
if (hardware == AD1848_HW_THINKPAD) { |
968 |
|
chip->thinkpad_flag = 1; |
969 |
|
chip->hardware = AD1848_HW_DETECT; /* reset */ |
970 |
|
snd_ad1848_thinkpad_twiddle(chip, 1); |
971 |
|
#ifdef CONFIG_PM |
972 |
|
chip->thinkpad_pmstate = pm_register(PM_ISA_DEV, 0, snd_ad1848_pm_callback); |
973 |
|
if (chip->thinkpad_pmstate) { |
974 |
|
chip->thinkpad_pmstate->data = chip; |
975 |
|
card->set_power_state = snd_ad1848_set_power_state; /* callback */ |
976 |
|
card->power_state_private_data = chip; |
977 |
|
} |
978 |
|
#endif |
979 |
|
} |
980 |
|
|
981 |
if (snd_ad1848_probe(chip) < 0) { |
if (snd_ad1848_probe(chip) < 0) { |
982 |
snd_ad1848_free(chip); |
snd_ad1848_free(chip); |
983 |
return -ENODEV; |
return -ENODEV; |