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 | |
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>
-rw-r--r-- | sound/pci/azt3328.c | 119 | ||||
-rw-r--r-- | sound/pci/azt3328.h | 20 |
2 files changed, 133 insertions, 6 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 |
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h index f489bdaf6d40..560a4653c0b2 100644 --- a/sound/pci/azt3328.h +++ b/sound/pci/azt3328.h | |||
@@ -5,6 +5,9 @@ | |||
5 | 5 | ||
6 | /*** main I/O area port indices ***/ | 6 | /*** main I/O area port indices ***/ |
7 | /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ | 7 | /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ |
8 | #define AZF_IO_SIZE_CODEC 0x80 | ||
9 | #define AZF_IO_SIZE_CODEC_PM 0x70 | ||
10 | |||
8 | /* the driver initialisation suggests a layout of 4 main areas: | 11 | /* the driver initialisation suggests a layout of 4 main areas: |
9 | * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). | 12 | * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). |
10 | * And another area from 0x60 to 0x6f (DirectX timer, IRQ management, | 13 | * And another area from 0x60 to 0x6f (DirectX timer, IRQ management, |
@@ -107,7 +110,8 @@ | |||
107 | #define IRQ_UNKNOWN2 0x0080 /* probably unused */ | 110 | #define IRQ_UNKNOWN2 0x0080 /* probably unused */ |
108 | #define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */ | 111 | #define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */ |
109 | #define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */ | 112 | #define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */ |
110 | #define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated; actually inhibits PCM playback!!! maybe power management?? */ | 113 | #define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */ |
114 | #define IO_6A_PAUSE_PLAYBACK 0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */ | ||
111 | #define IDX_IO_6CH 0x6C | 115 | #define IDX_IO_6CH 0x6C |
112 | #define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */ | 116 | #define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */ |
113 | /* further I/O indices not saved/restored, so probably not used */ | 117 | /* further I/O indices not saved/restored, so probably not used */ |
@@ -115,15 +119,25 @@ | |||
115 | 119 | ||
116 | /*** I/O 2 area port indices ***/ | 120 | /*** I/O 2 area port indices ***/ |
117 | /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ | 121 | /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ |
122 | #define AZF_IO_SIZE_IO2 0x08 | ||
123 | #define AZF_IO_SIZE_IO2_PM 0x06 | ||
124 | |||
118 | #define IDX_IO2_LEGACY_ADDR 0x04 | 125 | #define IDX_IO2_LEGACY_ADDR 0x04 |
119 | #define LEGACY_SOMETHING 0x01 /* OPL3?? */ | 126 | #define LEGACY_SOMETHING 0x01 /* OPL3?? */ |
120 | #define LEGACY_JOY 0x08 | 127 | #define LEGACY_JOY 0x08 |
121 | 128 | ||
129 | #define AZF_IO_SIZE_MPU 0x04 | ||
130 | #define AZF_IO_SIZE_MPU_PM 0x04 | ||
131 | |||
132 | #define AZF_IO_SIZE_SYNTH 0x08 | ||
133 | #define AZF_IO_SIZE_SYNTH_PM 0x06 | ||
122 | 134 | ||
123 | /*** mixer I/O area port indices ***/ | 135 | /*** mixer I/O area port indices ***/ |
124 | /* (only 0x22 of 0x40 bytes saved/restored by Windows driver) | 136 | /* (only 0x22 of 0x40 bytes saved/restored by Windows driver) |
125 | * generally spoken: AC97 register index = AZF3328 mixer reg index + 2 | 137 | * UNFORTUNATELY azf3328 is NOT truly AC97 compliant: see main file intro */ |
126 | * (in other words: AZF3328 NOT fully AC97 compliant) */ | 138 | #define AZF_IO_SIZE_MIXER 0x40 |
139 | #define AZF_IO_SIZE_MIXER_PM 0x22 | ||
140 | |||
127 | #define MIXER_VOLUME_RIGHT_MASK 0x001f | 141 | #define MIXER_VOLUME_RIGHT_MASK 0x001f |
128 | #define MIXER_VOLUME_LEFT_MASK 0x1f00 | 142 | #define MIXER_VOLUME_LEFT_MASK 0x1f00 |
129 | #define MIXER_MUTE_MASK 0x8000 | 143 | #define MIXER_MUTE_MASK 0x8000 |