diff options
author | Andreas Mohr <andi@lisas.de> | 2006-05-17 05:02:24 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-06-22 15:33:38 -0400 |
commit | ca54bde3634360afecd0dada9c59399bbe88bd32 (patch) | |
tree | 217d8c5f617da88191552433be734831edb6e6ed /sound/pci/azt3328.c | |
parent | 746df94898554b3d8e91d855e934852e626c701c (diff) |
[ALSA] azt3328.c: add suspend/resume support
- add suspend/resume handlers
- fix problem (private_data members not set)
Playing a file while suspending will resume correctly with this patch,
so I assume the hardware to get fully correctly reinitialized with
this patch.
Signed-off-by: Andreas Mohr <andi@lisas.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/azt3328.c')
-rw-r--r-- | sound/pci/azt3328.c | 119 |
1 files changed, 116 insertions, 3 deletions
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 52a364524262..f197fbac10ab 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c | |||
@@ -90,9 +90,11 @@ | |||
90 | * | 90 | * |
91 | * TODO | 91 | * TODO |
92 | * - test MPU401 MIDI playback etc. | 92 | * - test MPU401 MIDI playback etc. |
93 | * - power management. See e.g. intel8x0 or cs4281. | 93 | * - add some power micro-management (disable various units of the card |
94 | * This would be nice since the chip runs a bit hot, and it's *required* | 94 | * as long as they're unused). However this requires I/O ports which I |
95 | * anyway for proper ACPI power management. | 95 | * haven't figured out yet and which thus might not even exist... |
96 | * The standard suspend/resume functionality could probably make use of | ||
97 | * some improvement, too... | ||
96 | * - figure out what all unknown port bits are responsible for | 98 | * - figure out what all unknown port bits are responsible for |
97 | */ | 99 | */ |
98 | 100 | ||
@@ -214,6 +216,16 @@ struct snd_azf3328 { | |||
214 | 216 | ||
215 | struct pci_dev *pci; | 217 | struct pci_dev *pci; |
216 | int irq; | 218 | int irq; |
219 | |||
220 | #ifdef CONFIG_PM | ||
221 | /* register value containers for power management | ||
222 | * Note: not always full I/O range preserved (just like Win driver!) */ | ||
223 | u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2]; | ||
224 | u16 saved_regs_io2 [AZF_IO_SIZE_IO2_PM / 2]; | ||
225 | u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2]; | ||
226 | u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2]; | ||
227 | u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2]; | ||
228 | #endif | ||
217 | }; | 229 | }; |
218 | 230 | ||
219 | static const struct pci_device_id snd_azf3328_ids[] __devinitdata = { | 231 | static const struct pci_device_id snd_azf3328_ids[] __devinitdata = { |
@@ -961,6 +973,13 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
961 | chip->is_playing = 1; | 973 | chip->is_playing = 1; |
962 | snd_azf3328_dbgplay("STARTED PLAYBACK\n"); | 974 | snd_azf3328_dbgplay("STARTED PLAYBACK\n"); |
963 | break; | 975 | break; |
976 | case SNDRV_PCM_TRIGGER_RESUME: | ||
977 | snd_azf3328_dbgplay("RESUME PLAYBACK\n"); | ||
978 | /* resume playback if we were active */ | ||
979 | if (chip->is_playing) | ||
980 | snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, | ||
981 | snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME); | ||
982 | break; | ||
964 | case SNDRV_PCM_TRIGGER_STOP: | 983 | case SNDRV_PCM_TRIGGER_STOP: |
965 | snd_azf3328_dbgplay("STOP PLAYBACK\n"); | 984 | snd_azf3328_dbgplay("STOP PLAYBACK\n"); |
966 | 985 | ||
@@ -988,6 +1007,12 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
988 | chip->is_playing = 0; | 1007 | chip->is_playing = 0; |
989 | snd_azf3328_dbgplay("STOPPED PLAYBACK\n"); | 1008 | snd_azf3328_dbgplay("STOPPED PLAYBACK\n"); |
990 | break; | 1009 | break; |
1010 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
1011 | snd_azf3328_dbgplay("SUSPEND PLAYBACK\n"); | ||
1012 | /* make sure playback is stopped */ | ||
1013 | snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, | ||
1014 | snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME); | ||
1015 | break; | ||
991 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 1016 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
992 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); | 1017 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); |
993 | break; | 1018 | break; |
@@ -995,6 +1020,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
995 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); | 1020 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); |
996 | break; | 1021 | break; |
997 | default: | 1022 | default: |
1023 | printk(KERN_ERR "FIXME: unknown trigger mode!\n"); | ||
998 | return -EINVAL; | 1024 | return -EINVAL; |
999 | } | 1025 | } |
1000 | 1026 | ||
@@ -1068,6 +1094,13 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) | |||
1068 | chip->is_recording = 1; | 1094 | chip->is_recording = 1; |
1069 | snd_azf3328_dbgplay("STARTED CAPTURE\n"); | 1095 | snd_azf3328_dbgplay("STARTED CAPTURE\n"); |
1070 | break; | 1096 | break; |
1097 | case SNDRV_PCM_TRIGGER_RESUME: | ||
1098 | snd_azf3328_dbgplay("RESUME CAPTURE\n"); | ||
1099 | /* resume recording if we were active */ | ||
1100 | if (chip->is_recording) | ||
1101 | snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, | ||
1102 | snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME); | ||
1103 | break; | ||
1071 | case SNDRV_PCM_TRIGGER_STOP: | 1104 | case SNDRV_PCM_TRIGGER_STOP: |
1072 | snd_azf3328_dbgplay("STOP CAPTURE\n"); | 1105 | snd_azf3328_dbgplay("STOP CAPTURE\n"); |
1073 | 1106 | ||
@@ -1088,6 +1121,12 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) | |||
1088 | chip->is_recording = 0; | 1121 | chip->is_recording = 0; |
1089 | snd_azf3328_dbgplay("STOPPED CAPTURE\n"); | 1122 | snd_azf3328_dbgplay("STOPPED CAPTURE\n"); |
1090 | break; | 1123 | break; |
1124 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
1125 | snd_azf3328_dbgplay("SUSPEND CAPTURE\n"); | ||
1126 | /* make sure recording is stopped */ | ||
1127 | snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, | ||
1128 | snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME); | ||
1129 | break; | ||
1091 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 1130 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
1092 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); | 1131 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); |
1093 | break; | 1132 | break; |
@@ -1095,6 +1134,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) | |||
1095 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); | 1134 | snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); |
1096 | break; | 1135 | break; |
1097 | default: | 1136 | default: |
1137 | printk(KERN_ERR "FIXME: unknown trigger mode!\n"); | ||
1098 | return -EINVAL; | 1138 | return -EINVAL; |
1099 | } | 1139 | } |
1100 | 1140 | ||
@@ -1766,6 +1806,8 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) | |||
1766 | goto out_err; | 1806 | goto out_err; |
1767 | } | 1807 | } |
1768 | 1808 | ||
1809 | card->private_data = chip; | ||
1810 | |||
1769 | if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401, | 1811 | if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401, |
1770 | chip->mpu_port, 1, pci->irq, 0, | 1812 | chip->mpu_port, 1, pci->irq, 0, |
1771 | &chip->rmidi)) < 0) { | 1813 | &chip->rmidi)) < 0) { |
@@ -1791,6 +1833,8 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) | |||
1791 | } | 1833 | } |
1792 | } | 1834 | } |
1793 | 1835 | ||
1836 | opl3->private_data = chip; | ||
1837 | |||
1794 | sprintf(card->longname, "%s at 0x%lx, irq %i", | 1838 | sprintf(card->longname, "%s at 0x%lx, irq %i", |
1795 | card->shortname, chip->codec_port, chip->irq); | 1839 | card->shortname, chip->codec_port, chip->irq); |
1796 | 1840 | ||
@@ -1834,11 +1878,80 @@ snd_azf3328_remove(struct pci_dev *pci) | |||
1834 | snd_azf3328_dbgcallleave(); | 1878 | snd_azf3328_dbgcallleave(); |
1835 | } | 1879 | } |
1836 | 1880 | ||
1881 | #ifdef CONFIG_PM | ||
1882 | static int | ||
1883 | snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) | ||
1884 | { | ||
1885 | struct snd_card *card = pci_get_drvdata(pci); | ||
1886 | struct snd_azf3328 *chip = card->private_data; | ||
1887 | int reg; | ||
1888 | |||
1889 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
1890 | |||
1891 | snd_pcm_suspend_all(chip->pcm); | ||
1892 | |||
1893 | for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++) | ||
1894 | chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2); | ||
1895 | |||
1896 | /* make sure to disable master volume etc. to prevent looping sound */ | ||
1897 | snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); | ||
1898 | snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); | ||
1899 | |||
1900 | for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++) | ||
1901 | chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2); | ||
1902 | for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++) | ||
1903 | chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2); | ||
1904 | for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++) | ||
1905 | chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2); | ||
1906 | for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++) | ||
1907 | chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2); | ||
1908 | |||
1909 | pci_set_power_state(pci, PCI_D3hot); | ||
1910 | pci_disable_device(pci); | ||
1911 | pci_save_state(pci); | ||
1912 | return 0; | ||
1913 | } | ||
1914 | |||
1915 | static int | ||
1916 | snd_azf3328_resume(struct pci_dev *pci) | ||
1917 | { | ||
1918 | struct snd_card *card = pci_get_drvdata(pci); | ||
1919 | struct snd_azf3328 *chip = card->private_data; | ||
1920 | int reg; | ||
1921 | |||
1922 | pci_restore_state(pci); | ||
1923 | pci_enable_device(pci); | ||
1924 | pci_set_power_state(pci, PCI_D0); | ||
1925 | pci_set_master(pci); | ||
1926 | |||
1927 | for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++) | ||
1928 | outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2); | ||
1929 | for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++) | ||
1930 | outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2); | ||
1931 | for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++) | ||
1932 | outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2); | ||
1933 | for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++) | ||
1934 | outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2); | ||
1935 | for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++) | ||
1936 | outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2); | ||
1937 | |||
1938 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
1939 | return 0; | ||
1940 | } | ||
1941 | #endif | ||
1942 | |||
1943 | |||
1944 | |||
1945 | |||
1837 | static struct pci_driver driver = { | 1946 | static struct pci_driver driver = { |
1838 | .name = "AZF3328", | 1947 | .name = "AZF3328", |
1839 | .id_table = snd_azf3328_ids, | 1948 | .id_table = snd_azf3328_ids, |
1840 | .probe = snd_azf3328_probe, | 1949 | .probe = snd_azf3328_probe, |
1841 | .remove = __devexit_p(snd_azf3328_remove), | 1950 | .remove = __devexit_p(snd_azf3328_remove), |
1951 | #ifdef CONFIG_PM | ||
1952 | .suspend = snd_azf3328_suspend, | ||
1953 | .resume = snd_azf3328_resume, | ||
1954 | #endif | ||
1842 | }; | 1955 | }; |
1843 | 1956 | ||
1844 | static int __init | 1957 | static int __init |