diff options
Diffstat (limited to 'sound/isa')
76 files changed, 38300 insertions, 0 deletions
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig new file mode 100644 index 000000000000..4a6be966bd9f --- /dev/null +++ b/sound/isa/Kconfig | |||
@@ -0,0 +1,377 @@ | |||
1 | # ALSA ISA drivers | ||
2 | |||
3 | menu "ISA devices" | ||
4 | depends on SND!=n && ISA | ||
5 | |||
6 | config SND_AD1848_LIB | ||
7 | tristate | ||
8 | select SND_PCM | ||
9 | select SND_GENERIC_PM | ||
10 | |||
11 | config SND_CS4231_LIB | ||
12 | tristate | ||
13 | select SND_PCM | ||
14 | select SND_GENERIC_PM | ||
15 | |||
16 | config SND_AD1816A | ||
17 | tristate "Analog Devices SoundPort AD1816A" | ||
18 | depends on SND && ISAPNP | ||
19 | select SND_OPL3_LIB | ||
20 | select SND_MPU401_UART | ||
21 | select SND_PCM | ||
22 | help | ||
23 | Say Y here to include support for Analog Devices SoundPort | ||
24 | AD1816A or compatible sound chips. | ||
25 | |||
26 | To compile this driver as a module, choose M here: the module | ||
27 | will be called snd-ad1816a. | ||
28 | |||
29 | config SND_AD1848 | ||
30 | tristate "Generic AD1848/CS4248 driver" | ||
31 | depends on SND | ||
32 | select SND_AD1848_LIB | ||
33 | help | ||
34 | Say Y here to include support for AD1848 (Analog Devices) or | ||
35 | CS4248 (Cirrus Logic - Crystal Semiconductors) chips. | ||
36 | |||
37 | For newer chips from Cirrus Logic, use the CS4231, CS4232 or | ||
38 | CS4236+ drivers. | ||
39 | |||
40 | To compile this driver as a module, choose M here: the module | ||
41 | will be called snd-ad1848. | ||
42 | |||
43 | config SND_CS4231 | ||
44 | tristate "Generic Cirrus Logic CS4231 driver" | ||
45 | depends on SND | ||
46 | select SND_MPU401_UART | ||
47 | select SND_CS4231_LIB | ||
48 | help | ||
49 | Say Y here to include support for CS4231 chips from Cirrus | ||
50 | Logic - Crystal Semiconductors. | ||
51 | |||
52 | To compile this driver as a module, choose M here: the module | ||
53 | will be called snd-cs4231. | ||
54 | |||
55 | config SND_CS4232 | ||
56 | tristate "Generic Cirrus Logic CS4232 driver" | ||
57 | depends on SND | ||
58 | select SND_OPL3_LIB | ||
59 | select SND_MPU401_UART | ||
60 | select SND_CS4231_LIB | ||
61 | help | ||
62 | Say Y here to include support for CS4232 chips from Cirrus | ||
63 | Logic - Crystal Semiconductors. | ||
64 | |||
65 | To compile this driver as a module, choose M here: the module | ||
66 | will be called snd-cs4232. | ||
67 | |||
68 | config SND_CS4236 | ||
69 | tristate "Generic Cirrus Logic CS4236+ driver" | ||
70 | depends on SND | ||
71 | select SND_OPL3_LIB | ||
72 | select SND_MPU401_UART | ||
73 | select SND_CS4231_LIB | ||
74 | help | ||
75 | Say Y to include support for CS4235,CS4236,CS4237B,CS4238B, | ||
76 | CS4239 chips from Cirrus Logic - Crystal Semiconductors. | ||
77 | |||
78 | To compile this driver as a module, choose M here: the module | ||
79 | will be called snd-cs4236. | ||
80 | |||
81 | config SND_ES968 | ||
82 | tristate "Generic ESS ES968 driver" | ||
83 | depends on SND && ISAPNP | ||
84 | select SND_MPU401_UART | ||
85 | select SND_PCM | ||
86 | help | ||
87 | Say Y here to include support for ESS AudioDrive ES968 chips. | ||
88 | |||
89 | To compile this driver as a module, choose M here: the module | ||
90 | will be called snd-es968. | ||
91 | |||
92 | config SND_ES1688 | ||
93 | tristate "Generic ESS ES688/ES1688 driver" | ||
94 | depends on SND | ||
95 | select SND_OPL3_LIB | ||
96 | select SND_MPU401_UART | ||
97 | select SND_PCM | ||
98 | help | ||
99 | Say Y here to include support for ESS AudioDrive ES688 or | ||
100 | ES1688 chips. | ||
101 | |||
102 | To compile this driver as a module, choose M here: the module | ||
103 | will be called snd-es1688. | ||
104 | |||
105 | config SND_ES18XX | ||
106 | tristate "Generic ESS ES18xx driver" | ||
107 | depends on SND | ||
108 | select SND_OPL3_LIB | ||
109 | select SND_MPU401_UART | ||
110 | select SND_PCM | ||
111 | select SND_GENERIC_PM | ||
112 | help | ||
113 | Say Y here to include support for ESS AudioDrive ES18xx chips. | ||
114 | |||
115 | To compile this driver as a module, choose M here: the module | ||
116 | will be called snd-es18xx. | ||
117 | |||
118 | config SND_GUS_SYNTH | ||
119 | tristate | ||
120 | |||
121 | config SND_GUSCLASSIC | ||
122 | tristate "Gravis UltraSound Classic" | ||
123 | depends on SND | ||
124 | select SND_RAWMIDI | ||
125 | select SND_PCM | ||
126 | select SND_GUS_SYNTH | ||
127 | help | ||
128 | Say Y here to include support for Gravis UltraSound Classic | ||
129 | soundcards. | ||
130 | |||
131 | To compile this driver as a module, choose M here: the module | ||
132 | will be called snd-gusclassic. | ||
133 | |||
134 | config SND_GUSEXTREME | ||
135 | tristate "Gravis UltraSound Extreme" | ||
136 | depends on SND | ||
137 | select SND_HWDEP | ||
138 | select SND_MPU401_UART | ||
139 | select SND_PCM | ||
140 | select SND_GUS_SYNTH | ||
141 | help | ||
142 | Say Y here to include support for Gravis UltraSound Extreme | ||
143 | soundcards. | ||
144 | |||
145 | To compile this driver as a module, choose M here: the module | ||
146 | will be called snd-gusextreme. | ||
147 | |||
148 | config SND_GUSMAX | ||
149 | tristate "Gravis UltraSound MAX" | ||
150 | depends on SND | ||
151 | select SND_RAWMIDI | ||
152 | select SND_CS4231_LIB | ||
153 | select SND_GUS_SYNTH | ||
154 | help | ||
155 | Say Y here to include support for Gravis UltraSound MAX | ||
156 | soundcards. | ||
157 | |||
158 | To compile this driver as a module, choose M here: the module | ||
159 | will be called snd-gusmax. | ||
160 | |||
161 | config SND_INTERWAVE | ||
162 | tristate "AMD InterWave, Gravis UltraSound PnP" | ||
163 | depends on SND | ||
164 | select SND_RAWMIDI | ||
165 | select SND_CS4231_LIB | ||
166 | select SND_GUS_SYNTH | ||
167 | help | ||
168 | Say Y here to include support for AMD InterWave based | ||
169 | soundcards (Gravis UltraSound Plug & Play, STB SoundRage32, | ||
170 | MED3210, Dynasonic Pro, Panasonic PCA761AW). | ||
171 | |||
172 | To compile this driver as a module, choose M here: the module | ||
173 | will be called snd-interwave. | ||
174 | |||
175 | config SND_INTERWAVE_STB | ||
176 | tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)" | ||
177 | depends on SND | ||
178 | select SND_RAWMIDI | ||
179 | select SND_CS4231_LIB | ||
180 | select SND_GUS_SYNTH | ||
181 | help | ||
182 | Say Y here to include support for AMD InterWave based | ||
183 | soundcards with a TEA6330T bass and treble regulator | ||
184 | (UltraSound 32-Pro). | ||
185 | |||
186 | To compile this driver as a module, choose M here: the module | ||
187 | will be called snd-interwave-stb. | ||
188 | |||
189 | config SND_OPTI92X_AD1848 | ||
190 | tristate "OPTi 82C92x - AD1848" | ||
191 | depends on SND | ||
192 | select SND_OPL3_LIB | ||
193 | select SND_OPL4_LIB | ||
194 | select SND_MPU401_UART | ||
195 | select SND_AD1848_LIB | ||
196 | help | ||
197 | Say Y here to include support for soundcards based on Opti | ||
198 | 82C92x or OTI-601 chips and using an AD1848 codec. | ||
199 | |||
200 | To compile this driver as a module, choose M here: the module | ||
201 | will be called snd-opti92x-ad1848. | ||
202 | |||
203 | config SND_OPTI92X_CS4231 | ||
204 | tristate "OPTi 82C92x - CS4231" | ||
205 | depends on SND | ||
206 | select SND_OPL3_LIB | ||
207 | select SND_OPL4_LIB | ||
208 | select SND_MPU401_UART | ||
209 | select SND_CS4231_LIB | ||
210 | help | ||
211 | Say Y here to include support for soundcards based on Opti | ||
212 | 82C92x chips and using a CS4231 codec. | ||
213 | |||
214 | To compile this driver as a module, choose M here: the module | ||
215 | will be called snd-opti92x-cs4231. | ||
216 | |||
217 | config SND_OPTI93X | ||
218 | tristate "OPTi 82C93x" | ||
219 | depends on SND | ||
220 | select SND_OPL3_LIB | ||
221 | select SND_MPU401_UART | ||
222 | select SND_PCM | ||
223 | help | ||
224 | Say Y here to include support for soundcards based on Opti | ||
225 | 82C93x chips. | ||
226 | |||
227 | To compile this driver as a module, choose M here: the module | ||
228 | will be called snd-opti93x. | ||
229 | |||
230 | config SND_SB8 | ||
231 | tristate "Sound Blaster 1.0/2.0/Pro (8-bit)" | ||
232 | depends on SND | ||
233 | select SND_OPL3_LIB | ||
234 | select SND_RAWMIDI | ||
235 | select SND_PCM | ||
236 | help | ||
237 | Say Y here to include support for Creative Sound Blaster 1.0/ | ||
238 | 2.0/Pro (8-bit) or 100% compatible soundcards. | ||
239 | |||
240 | To compile this driver as a module, choose M here: the module | ||
241 | will be called snd-sb8. | ||
242 | |||
243 | config SND_SB16 | ||
244 | tristate "Sound Blaster 16 (PnP)" | ||
245 | depends on SND | ||
246 | select SND_OPL3_LIB | ||
247 | select SND_MPU401_UART | ||
248 | select SND_PCM | ||
249 | help | ||
250 | Say Y here to include support for Sound Blaster 16 soundcards | ||
251 | (including the Plug and Play version). | ||
252 | |||
253 | To compile this driver as a module, choose M here: the module | ||
254 | will be called snd-sb16. | ||
255 | |||
256 | config SND_SBAWE | ||
257 | tristate "Sound Blaster AWE (32,64) (PnP)" | ||
258 | depends on SND | ||
259 | select SND_OPL3_LIB | ||
260 | select SND_MPU401_UART | ||
261 | select SND_PCM | ||
262 | help | ||
263 | Say Y here to include support for Sound Blaster AWE soundcards | ||
264 | (including the Plug and Play version). | ||
265 | |||
266 | To compile this driver as a module, choose M here: the module | ||
267 | will be called snd-sbawe. | ||
268 | |||
269 | config SND_SB16_CSP | ||
270 | bool "Sound Blaster 16/AWE CSP support" | ||
271 | depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC) | ||
272 | help | ||
273 | Say Y here to include support for the CSP core. This special | ||
274 | coprocessor can do variable tasks like various compression and | ||
275 | decompression algorithms. | ||
276 | |||
277 | config SND_WAVEFRONT | ||
278 | tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)" | ||
279 | depends on SND | ||
280 | select SND_OPL3_LIB | ||
281 | select SND_MPU401_UART | ||
282 | select SND_CS4231_LIB | ||
283 | help | ||
284 | Say Y here to include support for Turtle Beach Maui, Tropez | ||
285 | and Tropez+ soundcards based on the Wavefront chip. | ||
286 | |||
287 | To compile this driver as a module, choose M here: the module | ||
288 | will be called snd-wavefront. | ||
289 | |||
290 | config SND_ALS100 | ||
291 | tristate "Avance Logic ALS100/ALS120" | ||
292 | depends on SND && ISAPNP | ||
293 | select SND_OPL3_LIB | ||
294 | select SND_MPU401_UART | ||
295 | select SND_PCM | ||
296 | help | ||
297 | Say Y here to include support for soundcards based on Avance | ||
298 | Logic ALS100, ALS110, ALS120 and ALS200 chips. | ||
299 | |||
300 | To compile this driver as a module, choose M here: the module | ||
301 | will be called snd-als100. | ||
302 | |||
303 | config SND_AZT2320 | ||
304 | tristate "Aztech Systems AZT2320" | ||
305 | depends on SND && ISAPNP | ||
306 | select SND_OPL3_LIB | ||
307 | select SND_MPU401_UART | ||
308 | select SND_CS4231_LIB | ||
309 | help | ||
310 | Say Y here to include support for soundcards based on the | ||
311 | Aztech Systems AZT2320 chip. | ||
312 | |||
313 | To compile this driver as a module, choose M here: the module | ||
314 | will be called snd-azt2320. | ||
315 | |||
316 | config SND_CMI8330 | ||
317 | tristate "C-Media CMI8330" | ||
318 | depends on SND | ||
319 | select SND_AD1848_LIB | ||
320 | help | ||
321 | Say Y here to include support for soundcards based on the | ||
322 | C-Media CMI8330 chip. | ||
323 | |||
324 | To compile this driver as a module, choose M here: the module | ||
325 | will be called snd-cmi8330. | ||
326 | |||
327 | config SND_DT019X | ||
328 | tristate "Diamond Technologies DT-019X, Avance Logic ALS-007" | ||
329 | depends on SND && ISAPNP | ||
330 | select SND_OPL3_LIB | ||
331 | select SND_MPU401_UART | ||
332 | select SND_PCM | ||
333 | help | ||
334 | Say Y here to include support for soundcards based on the | ||
335 | Diamond Technologies DT-019X or Avance Logic ALS-007 chips. | ||
336 | |||
337 | To compile this driver as a module, choose M here: the module | ||
338 | will be called snd-dt019x. | ||
339 | |||
340 | config SND_OPL3SA2 | ||
341 | tristate "Yamaha OPL3-SA2/SA3" | ||
342 | depends on SND | ||
343 | select SND_OPL3_LIB | ||
344 | select SND_MPU401_UART | ||
345 | select SND_CS4231_LIB | ||
346 | help | ||
347 | Say Y here to include support for Yamaha OPL3-SA2 and OPL3-SA3 | ||
348 | chips. | ||
349 | |||
350 | To compile this driver as a module, choose M here: the module | ||
351 | will be called snd-opl3sa2. | ||
352 | |||
353 | config SND_SGALAXY | ||
354 | tristate "Aztech Sound Galaxy" | ||
355 | depends on SND | ||
356 | select SND_AD1848_LIB | ||
357 | help | ||
358 | Say Y here to include support for Aztech Sound Galaxy | ||
359 | soundcards. | ||
360 | |||
361 | To compile this driver as a module, choose M here: the module | ||
362 | will be called snd-sgalaxy. | ||
363 | |||
364 | config SND_SSCAPE | ||
365 | tristate "Ensoniq SoundScape PnP driver" | ||
366 | depends on SND | ||
367 | select SND_HWDEP | ||
368 | select SND_MPU401_UART | ||
369 | select SND_CS4231_LIB | ||
370 | help | ||
371 | Say Y here to include support for Ensoniq SoundScape PnP | ||
372 | soundcards. | ||
373 | |||
374 | To compile this driver as a module, choose M here: the module | ||
375 | will be called snd-sscape. | ||
376 | |||
377 | endmenu | ||
diff --git a/sound/isa/Makefile b/sound/isa/Makefile new file mode 100644 index 000000000000..05724eb7bfe4 --- /dev/null +++ b/sound/isa/Makefile | |||
@@ -0,0 +1,26 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-als100-objs := als100.o | ||
7 | snd-azt2320-objs := azt2320.o | ||
8 | snd-cmi8330-objs := cmi8330.o | ||
9 | snd-dt019x-objs := dt019x.o | ||
10 | snd-es18xx-objs := es18xx.o | ||
11 | snd-opl3sa2-objs := opl3sa2.o | ||
12 | snd-sgalaxy-objs := sgalaxy.o | ||
13 | snd-sscape-objs := sscape.o | ||
14 | |||
15 | # Toplevel Module Dependency | ||
16 | obj-$(CONFIG_SND_ALS100) += snd-als100.o | ||
17 | obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o | ||
18 | obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o | ||
19 | obj-$(CONFIG_SND_DT019X) += snd-dt019x.o | ||
20 | obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o | ||
21 | obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o | ||
22 | obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o | ||
23 | obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o | ||
24 | |||
25 | obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \ | ||
26 | sb/ wavefront/ | ||
diff --git a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile new file mode 100644 index 000000000000..a42b29cf8549 --- /dev/null +++ b/sound/isa/ad1816a/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-ad1816a-lib-objs := ad1816a_lib.o | ||
7 | snd-ad1816a-objs := ad1816a.o | ||
8 | |||
9 | # Toplevel Module Dependency | ||
10 | obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o snd-ad1816a-lib.o | ||
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c new file mode 100644 index 000000000000..9fa7a78da6c3 --- /dev/null +++ b/sound/isa/ad1816a/ad1816a.c | |||
@@ -0,0 +1,312 @@ | |||
1 | |||
2 | /* | ||
3 | card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. | ||
4 | Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it> | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/pnp.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/initval.h> | ||
29 | #include <sound/ad1816a.h> | ||
30 | #include <sound/mpu401.h> | ||
31 | #include <sound/opl3.h> | ||
32 | |||
33 | #define PFX "ad1816a: " | ||
34 | |||
35 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
36 | MODULE_DESCRIPTION("AD1816A, AD1815"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | MODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D}," | ||
39 | "{Analog Devices,AD1815}," | ||
40 | "{Analog Devices,AD1816A}," | ||
41 | "{TerraTec,Base 64}," | ||
42 | "{TerraTec,AudioSystem EWS64S}," | ||
43 | "{Aztech/Newcom SC-16 3D}," | ||
44 | "{Shark Predator ISA}}"); | ||
45 | |||
46 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ | ||
47 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
48 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
49 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
50 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
51 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
52 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
53 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
54 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
55 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
56 | |||
57 | module_param_array(index, int, NULL, 0444); | ||
58 | MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard."); | ||
59 | module_param_array(id, charp, NULL, 0444); | ||
60 | MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard."); | ||
61 | module_param_array(enable, bool, NULL, 0444); | ||
62 | MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard."); | ||
63 | module_param_array(port, long, NULL, 0444); | ||
64 | MODULE_PARM_DESC(port, "Port # for ad1816a driver."); | ||
65 | module_param_array(mpu_port, long, NULL, 0444); | ||
66 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver."); | ||
67 | module_param_array(fm_port, long, NULL, 0444); | ||
68 | MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver."); | ||
69 | module_param_array(irq, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver."); | ||
71 | module_param_array(mpu_irq, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver."); | ||
73 | module_param_array(dma1, int, NULL, 0444); | ||
74 | MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver."); | ||
75 | module_param_array(dma2, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver."); | ||
77 | |||
78 | struct snd_card_ad1816a { | ||
79 | struct pnp_dev *dev; | ||
80 | struct pnp_dev *devmpu; | ||
81 | }; | ||
82 | |||
83 | static struct pnp_card_device_id snd_ad1816a_pnpids[] = { | ||
84 | /* Analog Devices AD1815 */ | ||
85 | { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } }, | ||
86 | /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */ | ||
87 | { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
88 | /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */ | ||
89 | { .id = "AZT1022", .devs = { { .id = "AZT1018" }, { .id = "AZT2002" } } }, | ||
90 | /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */ | ||
91 | { .id = "LWC1061", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
92 | /* Highscreen Sound-Boostar 16 3D */ | ||
93 | { .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
94 | /* Shark Predator ISA - added by Ken Arromdee */ | ||
95 | { .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
96 | /* Analog Devices AD1816A - Terratec AudioSystem EWS64S */ | ||
97 | { .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
98 | /* Analog Devices AD1816A - Terratec Base 64 */ | ||
99 | { .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, | ||
100 | /* end */ | ||
101 | { .id = "" } | ||
102 | }; | ||
103 | |||
104 | MODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids); | ||
105 | |||
106 | |||
107 | #define DRIVER_NAME "snd-card-ad1816a" | ||
108 | |||
109 | |||
110 | static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acard, | ||
111 | struct pnp_card_link *card, | ||
112 | const struct pnp_card_device_id *id) | ||
113 | { | ||
114 | struct pnp_dev *pdev; | ||
115 | struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); | ||
116 | int err; | ||
117 | |||
118 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
119 | if (acard->dev == NULL) { | ||
120 | kfree(cfg); | ||
121 | return -EBUSY; | ||
122 | } | ||
123 | acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
124 | if (acard->devmpu == NULL) { | ||
125 | kfree(cfg); | ||
126 | return -EBUSY; | ||
127 | } | ||
128 | |||
129 | pdev = acard->dev; | ||
130 | pnp_init_resource_table(cfg); | ||
131 | |||
132 | if (port[dev] != SNDRV_AUTO_PORT) | ||
133 | pnp_resource_change(&cfg->port_resource[2], port[dev], 16); | ||
134 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
135 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
136 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
137 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
138 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
139 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
140 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
141 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
142 | |||
143 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
144 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
145 | err = pnp_activate_dev(pdev); | ||
146 | if (err < 0) { | ||
147 | printk(KERN_ERR PFX "AUDIO PnP configure failure\n"); | ||
148 | kfree(cfg); | ||
149 | return -EBUSY; | ||
150 | } | ||
151 | |||
152 | port[dev] = pnp_port_start(pdev, 2); | ||
153 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
154 | dma1[dev] = pnp_dma(pdev, 0); | ||
155 | dma2[dev] = pnp_dma(pdev, 1); | ||
156 | irq[dev] = pnp_irq(pdev, 0); | ||
157 | |||
158 | pdev = acard->devmpu; | ||
159 | pnp_init_resource_table(cfg); | ||
160 | |||
161 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
162 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
163 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ) | ||
164 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
165 | |||
166 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
167 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
168 | err = pnp_activate_dev(pdev); | ||
169 | if (err < 0) { | ||
170 | printk(KERN_ERR PFX "MPU401 PnP configure failure\n"); | ||
171 | mpu_port[dev] = -1; | ||
172 | acard->devmpu = NULL; | ||
173 | } else { | ||
174 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
175 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
176 | } | ||
177 | |||
178 | kfree(cfg); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, | ||
183 | const struct pnp_card_device_id *pid) | ||
184 | { | ||
185 | int error; | ||
186 | snd_card_t *card; | ||
187 | struct snd_card_ad1816a *acard; | ||
188 | ad1816a_t *chip; | ||
189 | opl3_t *opl3; | ||
190 | |||
191 | if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
192 | sizeof(struct snd_card_ad1816a))) == NULL) | ||
193 | return -ENOMEM; | ||
194 | acard = (struct snd_card_ad1816a *)card->private_data; | ||
195 | |||
196 | if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) { | ||
197 | snd_card_free(card); | ||
198 | return error; | ||
199 | } | ||
200 | snd_card_set_dev(card, &pcard->card->dev); | ||
201 | |||
202 | if ((error = snd_ad1816a_create(card, port[dev], | ||
203 | irq[dev], | ||
204 | dma1[dev], | ||
205 | dma2[dev], | ||
206 | &chip)) < 0) { | ||
207 | snd_card_free(card); | ||
208 | return error; | ||
209 | } | ||
210 | |||
211 | strcpy(card->driver, "AD1816A"); | ||
212 | strcpy(card->shortname, "ADI SoundPort AD1816A"); | ||
213 | sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d", | ||
214 | card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); | ||
215 | |||
216 | if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) { | ||
217 | snd_card_free(card); | ||
218 | return error; | ||
219 | } | ||
220 | |||
221 | if ((error = snd_ad1816a_mixer(chip)) < 0) { | ||
222 | snd_card_free(card); | ||
223 | return error; | ||
224 | } | ||
225 | |||
226 | if (mpu_port[dev] > 0) { | ||
227 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, | ||
228 | mpu_port[dev], 0, mpu_irq[dev], SA_INTERRUPT, | ||
229 | NULL) < 0) | ||
230 | printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]); | ||
231 | } | ||
232 | |||
233 | if (fm_port[dev] > 0) { | ||
234 | if (snd_opl3_create(card, | ||
235 | fm_port[dev], fm_port[dev] + 2, | ||
236 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
237 | printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2); | ||
238 | } else { | ||
239 | if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { | ||
240 | snd_card_free(card); | ||
241 | return error; | ||
242 | } | ||
243 | if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
244 | snd_card_free(card); | ||
245 | return error; | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if ((error = snd_card_register(card)) < 0) { | ||
251 | snd_card_free(card); | ||
252 | return error; | ||
253 | } | ||
254 | pnp_set_card_drvdata(pcard, card); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card, | ||
259 | const struct pnp_card_device_id *id) | ||
260 | { | ||
261 | static int dev; | ||
262 | int res; | ||
263 | |||
264 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
265 | if (!enable[dev]) | ||
266 | continue; | ||
267 | res = snd_card_ad1816a_probe(dev, card, id); | ||
268 | if (res < 0) | ||
269 | return res; | ||
270 | dev++; | ||
271 | return 0; | ||
272 | } | ||
273 | return -ENODEV; | ||
274 | } | ||
275 | |||
276 | static void __devexit snd_ad1816a_pnp_remove(struct pnp_card_link * pcard) | ||
277 | { | ||
278 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
279 | |||
280 | snd_card_disconnect(card); | ||
281 | snd_card_free_in_thread(card); | ||
282 | } | ||
283 | |||
284 | static struct pnp_card_driver ad1816a_pnpc_driver = { | ||
285 | .flags = PNP_DRIVER_RES_DISABLE, | ||
286 | .name = "ad1816a", | ||
287 | .id_table = snd_ad1816a_pnpids, | ||
288 | .probe = snd_ad1816a_pnp_detect, | ||
289 | .remove = __devexit_p(snd_ad1816a_pnp_remove), | ||
290 | }; | ||
291 | |||
292 | static int __init alsa_card_ad1816a_init(void) | ||
293 | { | ||
294 | int cards = 0; | ||
295 | |||
296 | cards += pnp_register_card_driver(&ad1816a_pnpc_driver); | ||
297 | #ifdef MODULE | ||
298 | if (!cards) { | ||
299 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); | ||
300 | printk(KERN_ERR "no AD1816A based soundcards found.\n"); | ||
301 | } | ||
302 | #endif /* MODULE */ | ||
303 | return cards ? 0 : -ENODEV; | ||
304 | } | ||
305 | |||
306 | static void __exit alsa_card_ad1816a_exit(void) | ||
307 | { | ||
308 | pnp_unregister_card_driver(&ad1816a_pnpc_driver); | ||
309 | } | ||
310 | |||
311 | module_init(alsa_card_ad1816a_init) | ||
312 | module_exit(alsa_card_ad1816a_exit) | ||
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c new file mode 100644 index 000000000000..625b2eff14a1 --- /dev/null +++ b/sound/isa/ad1816a/ad1816a_lib.c | |||
@@ -0,0 +1,974 @@ | |||
1 | |||
2 | /* | ||
3 | ad1816a.c - lowlevel code for Analog Devices AD1816A chip. | ||
4 | Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/ad1816a.h> | ||
29 | |||
30 | #include <asm/io.h> | ||
31 | #include <asm/dma.h> | ||
32 | |||
33 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
34 | MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | static inline int snd_ad1816a_busy_wait(ad1816a_t *chip) | ||
38 | { | ||
39 | int timeout; | ||
40 | |||
41 | for (timeout = 1000; timeout-- > 0; udelay(10)) | ||
42 | if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) | ||
43 | return 0; | ||
44 | |||
45 | snd_printk("chip busy.\n"); | ||
46 | return -EBUSY; | ||
47 | } | ||
48 | |||
49 | static inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg) | ||
50 | { | ||
51 | snd_ad1816a_busy_wait(chip); | ||
52 | return inb(AD1816A_REG(reg)); | ||
53 | } | ||
54 | |||
55 | static inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg, | ||
56 | unsigned char value) | ||
57 | { | ||
58 | snd_ad1816a_busy_wait(chip); | ||
59 | outb(value, AD1816A_REG(reg)); | ||
60 | } | ||
61 | |||
62 | static inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg, | ||
63 | unsigned char mask, unsigned char value) | ||
64 | { | ||
65 | snd_ad1816a_out(chip, reg, | ||
66 | (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); | ||
67 | } | ||
68 | |||
69 | static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg) | ||
70 | { | ||
71 | snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); | ||
72 | return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | | ||
73 | (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); | ||
74 | } | ||
75 | |||
76 | static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg, | ||
77 | unsigned short value) | ||
78 | { | ||
79 | snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); | ||
80 | snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); | ||
81 | snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); | ||
82 | } | ||
83 | |||
84 | static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg, | ||
85 | unsigned short mask, unsigned short value) | ||
86 | { | ||
87 | snd_ad1816a_write(chip, reg, | ||
88 | (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); | ||
89 | } | ||
90 | |||
91 | |||
92 | static unsigned char snd_ad1816a_get_format(ad1816a_t *chip, | ||
93 | unsigned int format, int channels) | ||
94 | { | ||
95 | unsigned char retval = AD1816A_FMT_LINEAR_8; | ||
96 | |||
97 | switch (format) { | ||
98 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
99 | retval = AD1816A_FMT_ULAW_8; | ||
100 | break; | ||
101 | case SNDRV_PCM_FORMAT_A_LAW: | ||
102 | retval = AD1816A_FMT_ALAW_8; | ||
103 | break; | ||
104 | case SNDRV_PCM_FORMAT_S16_LE: | ||
105 | retval = AD1816A_FMT_LINEAR_16_LIT; | ||
106 | break; | ||
107 | case SNDRV_PCM_FORMAT_S16_BE: | ||
108 | retval = AD1816A_FMT_LINEAR_16_BIG; | ||
109 | } | ||
110 | return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; | ||
111 | } | ||
112 | |||
113 | static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | |||
117 | spin_lock_irqsave(&chip->lock, flags); | ||
118 | |||
119 | if (chip->mode & mode) { | ||
120 | spin_unlock_irqrestore(&chip->lock, flags); | ||
121 | return -EAGAIN; | ||
122 | } | ||
123 | |||
124 | switch ((mode &= AD1816A_MODE_OPEN)) { | ||
125 | case AD1816A_MODE_PLAYBACK: | ||
126 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
127 | AD1816A_PLAYBACK_IRQ_PENDING, 0x00); | ||
128 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
129 | AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); | ||
130 | break; | ||
131 | case AD1816A_MODE_CAPTURE: | ||
132 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
133 | AD1816A_CAPTURE_IRQ_PENDING, 0x00); | ||
134 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
135 | AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); | ||
136 | break; | ||
137 | case AD1816A_MODE_TIMER: | ||
138 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
139 | AD1816A_TIMER_IRQ_PENDING, 0x00); | ||
140 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
141 | AD1816A_TIMER_IRQ_ENABLE, 0xffff); | ||
142 | } | ||
143 | chip->mode |= mode; | ||
144 | |||
145 | spin_unlock_irqrestore(&chip->lock, flags); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode) | ||
150 | { | ||
151 | unsigned long flags; | ||
152 | |||
153 | spin_lock_irqsave(&chip->lock, flags); | ||
154 | |||
155 | switch ((mode &= AD1816A_MODE_OPEN)) { | ||
156 | case AD1816A_MODE_PLAYBACK: | ||
157 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
158 | AD1816A_PLAYBACK_IRQ_PENDING, 0x00); | ||
159 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
160 | AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); | ||
161 | break; | ||
162 | case AD1816A_MODE_CAPTURE: | ||
163 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
164 | AD1816A_CAPTURE_IRQ_PENDING, 0x00); | ||
165 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
166 | AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); | ||
167 | break; | ||
168 | case AD1816A_MODE_TIMER: | ||
169 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
170 | AD1816A_TIMER_IRQ_PENDING, 0x00); | ||
171 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
172 | AD1816A_TIMER_IRQ_ENABLE, 0x0000); | ||
173 | } | ||
174 | if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN)) | ||
175 | chip->mode = 0; | ||
176 | |||
177 | spin_unlock_irqrestore(&chip->lock, flags); | ||
178 | } | ||
179 | |||
180 | |||
181 | static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what, | ||
182 | int channel, int cmd) | ||
183 | { | ||
184 | int error = 0; | ||
185 | |||
186 | switch (cmd) { | ||
187 | case SNDRV_PCM_TRIGGER_START: | ||
188 | case SNDRV_PCM_TRIGGER_STOP: | ||
189 | spin_lock(&chip->lock); | ||
190 | cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; | ||
191 | if (what & AD1816A_PLAYBACK_ENABLE) | ||
192 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
193 | AD1816A_PLAYBACK_ENABLE, cmd); | ||
194 | if (what & AD1816A_CAPTURE_ENABLE) | ||
195 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
196 | AD1816A_CAPTURE_ENABLE, cmd); | ||
197 | spin_unlock(&chip->lock); | ||
198 | break; | ||
199 | default: | ||
200 | snd_printk("invalid trigger mode 0x%x.\n", what); | ||
201 | error = -EINVAL; | ||
202 | } | ||
203 | |||
204 | return error; | ||
205 | } | ||
206 | |||
207 | static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd) | ||
208 | { | ||
209 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
210 | return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, | ||
211 | SNDRV_PCM_STREAM_PLAYBACK, cmd); | ||
212 | } | ||
213 | |||
214 | static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd) | ||
215 | { | ||
216 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
217 | return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, | ||
218 | SNDRV_PCM_STREAM_CAPTURE, cmd); | ||
219 | } | ||
220 | |||
221 | static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream, | ||
222 | snd_pcm_hw_params_t * hw_params) | ||
223 | { | ||
224 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
225 | } | ||
226 | |||
227 | static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream) | ||
228 | { | ||
229 | return snd_pcm_lib_free_pages(substream); | ||
230 | } | ||
231 | |||
232 | static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) | ||
233 | { | ||
234 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
235 | unsigned long flags; | ||
236 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
237 | unsigned int size; | ||
238 | |||
239 | spin_lock_irqsave(&chip->lock, flags); | ||
240 | |||
241 | chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); | ||
242 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
243 | AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); | ||
244 | |||
245 | snd_dma_program(chip->dma1, runtime->dma_addr, size, | ||
246 | DMA_MODE_WRITE | DMA_AUTOINIT); | ||
247 | |||
248 | snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); | ||
249 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
250 | AD1816A_FMT_ALL | AD1816A_FMT_STEREO, | ||
251 | snd_ad1816a_get_format(chip, runtime->format, | ||
252 | runtime->channels)); | ||
253 | |||
254 | snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, | ||
255 | snd_pcm_lib_period_bytes(substream) / 4 - 1); | ||
256 | |||
257 | spin_unlock_irqrestore(&chip->lock, flags); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) | ||
262 | { | ||
263 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
264 | unsigned long flags; | ||
265 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
266 | unsigned int size; | ||
267 | |||
268 | spin_lock_irqsave(&chip->lock, flags); | ||
269 | |||
270 | chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); | ||
271 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
272 | AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); | ||
273 | |||
274 | snd_dma_program(chip->dma2, runtime->dma_addr, size, | ||
275 | DMA_MODE_READ | DMA_AUTOINIT); | ||
276 | |||
277 | snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); | ||
278 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
279 | AD1816A_FMT_ALL | AD1816A_FMT_STEREO, | ||
280 | snd_ad1816a_get_format(chip, runtime->format, | ||
281 | runtime->channels)); | ||
282 | |||
283 | snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, | ||
284 | snd_pcm_lib_period_bytes(substream) / 4 - 1); | ||
285 | |||
286 | spin_unlock_irqrestore(&chip->lock, flags); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | |||
291 | static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream) | ||
292 | { | ||
293 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
294 | size_t ptr; | ||
295 | if (!(chip->mode & AD1816A_MODE_PLAYBACK)) | ||
296 | return 0; | ||
297 | ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); | ||
298 | return bytes_to_frames(substream->runtime, ptr); | ||
299 | } | ||
300 | |||
301 | static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream) | ||
302 | { | ||
303 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
304 | size_t ptr; | ||
305 | if (!(chip->mode & AD1816A_MODE_CAPTURE)) | ||
306 | return 0; | ||
307 | ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); | ||
308 | return bytes_to_frames(substream->runtime, ptr); | ||
309 | } | ||
310 | |||
311 | |||
312 | static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
313 | { | ||
314 | ad1816a_t *chip = dev_id; | ||
315 | unsigned char status; | ||
316 | |||
317 | spin_lock(&chip->lock); | ||
318 | status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); | ||
319 | spin_unlock(&chip->lock); | ||
320 | |||
321 | if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) | ||
322 | snd_pcm_period_elapsed(chip->playback_substream); | ||
323 | |||
324 | if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) | ||
325 | snd_pcm_period_elapsed(chip->capture_substream); | ||
326 | |||
327 | if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) | ||
328 | snd_timer_interrupt(chip->timer, chip->timer->sticks); | ||
329 | |||
330 | spin_lock(&chip->lock); | ||
331 | snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); | ||
332 | spin_unlock(&chip->lock); | ||
333 | return IRQ_HANDLED; | ||
334 | } | ||
335 | |||
336 | |||
337 | static snd_pcm_hardware_t snd_ad1816a_playback = { | ||
338 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
339 | SNDRV_PCM_INFO_MMAP_VALID), | ||
340 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
341 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | | ||
342 | SNDRV_PCM_FMTBIT_S16_BE), | ||
343 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
344 | .rate_min = 4000, | ||
345 | .rate_max = 55200, | ||
346 | .channels_min = 1, | ||
347 | .channels_max = 2, | ||
348 | .buffer_bytes_max = (128*1024), | ||
349 | .period_bytes_min = 64, | ||
350 | .period_bytes_max = (128*1024), | ||
351 | .periods_min = 1, | ||
352 | .periods_max = 1024, | ||
353 | .fifo_size = 0, | ||
354 | }; | ||
355 | |||
356 | static snd_pcm_hardware_t snd_ad1816a_capture = { | ||
357 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
358 | SNDRV_PCM_INFO_MMAP_VALID), | ||
359 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
360 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | | ||
361 | SNDRV_PCM_FMTBIT_S16_BE), | ||
362 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
363 | .rate_min = 4000, | ||
364 | .rate_max = 55200, | ||
365 | .channels_min = 1, | ||
366 | .channels_max = 2, | ||
367 | .buffer_bytes_max = (128*1024), | ||
368 | .period_bytes_min = 64, | ||
369 | .period_bytes_max = (128*1024), | ||
370 | .periods_min = 1, | ||
371 | .periods_max = 1024, | ||
372 | .fifo_size = 0, | ||
373 | }; | ||
374 | |||
375 | #if 0 /* not used now */ | ||
376 | static int snd_ad1816a_timer_close(snd_timer_t *timer) | ||
377 | { | ||
378 | ad1816a_t *chip = snd_timer_chip(timer); | ||
379 | snd_ad1816a_close(chip, AD1816A_MODE_TIMER); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int snd_ad1816a_timer_open(snd_timer_t *timer) | ||
384 | { | ||
385 | ad1816a_t *chip = snd_timer_chip(timer); | ||
386 | snd_ad1816a_open(chip, AD1816A_MODE_TIMER); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer) | ||
391 | { | ||
392 | snd_assert(timer != NULL, return 0); | ||
393 | |||
394 | return 10000; | ||
395 | } | ||
396 | |||
397 | static int snd_ad1816a_timer_start(snd_timer_t *timer) | ||
398 | { | ||
399 | unsigned short bits; | ||
400 | unsigned long flags; | ||
401 | ad1816a_t *chip = snd_timer_chip(timer); | ||
402 | spin_lock_irqsave(&chip->lock, flags); | ||
403 | bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); | ||
404 | |||
405 | if (!(bits & AD1816A_TIMER_ENABLE)) { | ||
406 | snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, | ||
407 | timer->sticks & 0xffff); | ||
408 | |||
409 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
410 | AD1816A_TIMER_ENABLE, 0xffff); | ||
411 | } | ||
412 | spin_unlock_irqrestore(&chip->lock, flags); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static int snd_ad1816a_timer_stop(snd_timer_t *timer) | ||
417 | { | ||
418 | unsigned long flags; | ||
419 | ad1816a_t *chip = snd_timer_chip(timer); | ||
420 | spin_lock_irqsave(&chip->lock, flags); | ||
421 | |||
422 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
423 | AD1816A_TIMER_ENABLE, 0x0000); | ||
424 | |||
425 | spin_unlock_irqrestore(&chip->lock, flags); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static struct _snd_timer_hardware snd_ad1816a_timer_table = { | ||
430 | .flags = SNDRV_TIMER_HW_AUTO, | ||
431 | .resolution = 10000, | ||
432 | .ticks = 65535, | ||
433 | .open = snd_ad1816a_timer_open, | ||
434 | .close = snd_ad1816a_timer_close, | ||
435 | .c_resolution = snd_ad1816a_timer_resolution, | ||
436 | .start = snd_ad1816a_timer_start, | ||
437 | .stop = snd_ad1816a_timer_stop, | ||
438 | }; | ||
439 | #endif /* not used now */ | ||
440 | |||
441 | |||
442 | static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream) | ||
443 | { | ||
444 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
445 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
446 | int error; | ||
447 | |||
448 | if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) | ||
449 | return error; | ||
450 | snd_pcm_set_sync(substream); | ||
451 | runtime->hw = snd_ad1816a_playback; | ||
452 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); | ||
453 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); | ||
454 | chip->playback_substream = substream; | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream) | ||
459 | { | ||
460 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
461 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
462 | int error; | ||
463 | |||
464 | if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) | ||
465 | return error; | ||
466 | snd_pcm_set_sync(substream); | ||
467 | runtime->hw = snd_ad1816a_capture; | ||
468 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); | ||
469 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); | ||
470 | chip->capture_substream = substream; | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream) | ||
475 | { | ||
476 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
477 | |||
478 | chip->playback_substream = NULL; | ||
479 | snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream) | ||
484 | { | ||
485 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
486 | |||
487 | chip->capture_substream = NULL; | ||
488 | snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | |||
493 | static void snd_ad1816a_init(ad1816a_t *chip) | ||
494 | { | ||
495 | unsigned long flags; | ||
496 | |||
497 | spin_lock_irqsave(&chip->lock, flags); | ||
498 | |||
499 | snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); | ||
500 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
501 | AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); | ||
502 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
503 | AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); | ||
504 | snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); | ||
505 | snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, | ||
506 | AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); | ||
507 | snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); | ||
508 | snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); | ||
509 | |||
510 | spin_unlock_irqrestore(&chip->lock, flags); | ||
511 | } | ||
512 | |||
513 | static int snd_ad1816a_probe(ad1816a_t *chip) | ||
514 | { | ||
515 | unsigned long flags; | ||
516 | |||
517 | spin_lock_irqsave(&chip->lock, flags); | ||
518 | |||
519 | switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { | ||
520 | case 0: | ||
521 | chip->hardware = AD1816A_HW_AD1815; | ||
522 | break; | ||
523 | case 1: | ||
524 | chip->hardware = AD1816A_HW_AD18MAX10; | ||
525 | break; | ||
526 | case 3: | ||
527 | chip->hardware = AD1816A_HW_AD1816A; | ||
528 | break; | ||
529 | default: | ||
530 | chip->hardware = AD1816A_HW_AUTO; | ||
531 | } | ||
532 | |||
533 | spin_unlock_irqrestore(&chip->lock, flags); | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int snd_ad1816a_free(ad1816a_t *chip) | ||
538 | { | ||
539 | if (chip->res_port) { | ||
540 | release_resource(chip->res_port); | ||
541 | kfree_nocheck(chip->res_port); | ||
542 | } | ||
543 | if (chip->irq >= 0) | ||
544 | free_irq(chip->irq, (void *) chip); | ||
545 | if (chip->dma1 >= 0) { | ||
546 | snd_dma_disable(chip->dma1); | ||
547 | free_dma(chip->dma1); | ||
548 | } | ||
549 | if (chip->dma2 >= 0) { | ||
550 | snd_dma_disable(chip->dma2); | ||
551 | free_dma(chip->dma2); | ||
552 | } | ||
553 | kfree(chip); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int snd_ad1816a_dev_free(snd_device_t *device) | ||
558 | { | ||
559 | ad1816a_t *chip = device->device_data; | ||
560 | return snd_ad1816a_free(chip); | ||
561 | } | ||
562 | |||
563 | static const char *snd_ad1816a_chip_id(ad1816a_t *chip) | ||
564 | { | ||
565 | switch (chip->hardware) { | ||
566 | case AD1816A_HW_AD1816A: return "AD1816A"; | ||
567 | case AD1816A_HW_AD1815: return "AD1815"; | ||
568 | case AD1816A_HW_AD18MAX10: return "AD18max10"; | ||
569 | default: | ||
570 | snd_printk("Unknown chip version %d:%d.\n", | ||
571 | chip->version, chip->hardware); | ||
572 | return "AD1816A - unknown"; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | int snd_ad1816a_create(snd_card_t *card, | ||
577 | unsigned long port, int irq, int dma1, int dma2, | ||
578 | ad1816a_t **rchip) | ||
579 | { | ||
580 | static snd_device_ops_t ops = { | ||
581 | .dev_free = snd_ad1816a_dev_free, | ||
582 | }; | ||
583 | int error; | ||
584 | ad1816a_t *chip; | ||
585 | |||
586 | *rchip = NULL; | ||
587 | |||
588 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
589 | if (chip == NULL) | ||
590 | return -ENOMEM; | ||
591 | chip->irq = -1; | ||
592 | chip->dma1 = -1; | ||
593 | chip->dma2 = -1; | ||
594 | |||
595 | if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) { | ||
596 | snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port); | ||
597 | snd_ad1816a_free(chip); | ||
598 | return -EBUSY; | ||
599 | } | ||
600 | if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { | ||
601 | snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq); | ||
602 | snd_ad1816a_free(chip); | ||
603 | return -EBUSY; | ||
604 | } | ||
605 | chip->irq = irq; | ||
606 | if (request_dma(dma1, "AD1816A - 1")) { | ||
607 | snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1); | ||
608 | snd_ad1816a_free(chip); | ||
609 | return -EBUSY; | ||
610 | } | ||
611 | chip->dma1 = dma1; | ||
612 | if (request_dma(dma2, "AD1816A - 2")) { | ||
613 | snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2); | ||
614 | snd_ad1816a_free(chip); | ||
615 | return -EBUSY; | ||
616 | } | ||
617 | chip->dma2 = dma2; | ||
618 | |||
619 | chip->card = card; | ||
620 | chip->port = port; | ||
621 | spin_lock_init(&chip->lock); | ||
622 | |||
623 | if ((error = snd_ad1816a_probe(chip))) { | ||
624 | snd_ad1816a_free(chip); | ||
625 | return error; | ||
626 | } | ||
627 | |||
628 | snd_ad1816a_init(chip); | ||
629 | |||
630 | /* Register device */ | ||
631 | if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
632 | snd_ad1816a_free(chip); | ||
633 | return error; | ||
634 | } | ||
635 | |||
636 | *rchip = chip; | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | static snd_pcm_ops_t snd_ad1816a_playback_ops = { | ||
641 | .open = snd_ad1816a_playback_open, | ||
642 | .close = snd_ad1816a_playback_close, | ||
643 | .ioctl = snd_pcm_lib_ioctl, | ||
644 | .hw_params = snd_ad1816a_hw_params, | ||
645 | .hw_free = snd_ad1816a_hw_free, | ||
646 | .prepare = snd_ad1816a_playback_prepare, | ||
647 | .trigger = snd_ad1816a_playback_trigger, | ||
648 | .pointer = snd_ad1816a_playback_pointer, | ||
649 | }; | ||
650 | |||
651 | static snd_pcm_ops_t snd_ad1816a_capture_ops = { | ||
652 | .open = snd_ad1816a_capture_open, | ||
653 | .close = snd_ad1816a_capture_close, | ||
654 | .ioctl = snd_pcm_lib_ioctl, | ||
655 | .hw_params = snd_ad1816a_hw_params, | ||
656 | .hw_free = snd_ad1816a_hw_free, | ||
657 | .prepare = snd_ad1816a_capture_prepare, | ||
658 | .trigger = snd_ad1816a_capture_trigger, | ||
659 | .pointer = snd_ad1816a_capture_pointer, | ||
660 | }; | ||
661 | |||
662 | static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) | ||
663 | { | ||
664 | ad1816a_t *chip = pcm->private_data; | ||
665 | chip->pcm = NULL; | ||
666 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
667 | } | ||
668 | |||
669 | int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm) | ||
670 | { | ||
671 | int error; | ||
672 | snd_pcm_t *pcm; | ||
673 | |||
674 | if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm))) | ||
675 | return error; | ||
676 | |||
677 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); | ||
678 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); | ||
679 | |||
680 | pcm->private_data = chip; | ||
681 | pcm->private_free = snd_ad1816a_pcm_free; | ||
682 | pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; | ||
683 | |||
684 | strcpy(pcm->name, snd_ad1816a_chip_id(chip)); | ||
685 | snd_ad1816a_init(chip); | ||
686 | |||
687 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
688 | snd_dma_isa_data(), | ||
689 | 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); | ||
690 | |||
691 | chip->pcm = pcm; | ||
692 | if (rpcm) | ||
693 | *rpcm = pcm; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | #if 0 /* not used now */ | ||
698 | static void snd_ad1816a_timer_free(snd_timer_t *timer) | ||
699 | { | ||
700 | ad1816a_t *chip = timer->private_data; | ||
701 | chip->timer = NULL; | ||
702 | } | ||
703 | |||
704 | int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer) | ||
705 | { | ||
706 | snd_timer_t *timer; | ||
707 | snd_timer_id_t tid; | ||
708 | int error; | ||
709 | |||
710 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
711 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
712 | tid.card = chip->card->number; | ||
713 | tid.device = device; | ||
714 | tid.subdevice = 0; | ||
715 | if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0) | ||
716 | return error; | ||
717 | strcpy(timer->name, snd_ad1816a_chip_id(chip)); | ||
718 | timer->private_data = chip; | ||
719 | timer->private_free = snd_ad1816a_timer_free; | ||
720 | chip->timer = timer; | ||
721 | timer->hw = snd_ad1816a_timer_table; | ||
722 | if (rtimer) | ||
723 | *rtimer = timer; | ||
724 | return 0; | ||
725 | } | ||
726 | #endif /* not used now */ | ||
727 | |||
728 | /* | ||
729 | * | ||
730 | */ | ||
731 | |||
732 | static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
733 | { | ||
734 | static char *texts[8] = { | ||
735 | "Line", "Mix", "CD", "Synth", "Video", | ||
736 | "Mic", "Phone", | ||
737 | }; | ||
738 | |||
739 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
740 | uinfo->count = 2; | ||
741 | uinfo->value.enumerated.items = 7; | ||
742 | if (uinfo->value.enumerated.item > 6) | ||
743 | uinfo->value.enumerated.item = 6; | ||
744 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
749 | { | ||
750 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
751 | unsigned long flags; | ||
752 | unsigned short val; | ||
753 | |||
754 | spin_lock_irqsave(&chip->lock, flags); | ||
755 | val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); | ||
756 | spin_unlock_irqrestore(&chip->lock, flags); | ||
757 | ucontrol->value.enumerated.item[0] = (val >> 12) & 7; | ||
758 | ucontrol->value.enumerated.item[1] = (val >> 4) & 7; | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
763 | { | ||
764 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
765 | unsigned long flags; | ||
766 | unsigned short val; | ||
767 | int change; | ||
768 | |||
769 | if (ucontrol->value.enumerated.item[0] > 6 || | ||
770 | ucontrol->value.enumerated.item[1] > 6) | ||
771 | return -EINVAL; | ||
772 | val = (ucontrol->value.enumerated.item[0] << 12) | | ||
773 | (ucontrol->value.enumerated.item[1] << 4); | ||
774 | spin_lock_irqsave(&chip->lock, flags); | ||
775 | change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; | ||
776 | snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); | ||
777 | spin_unlock_irqrestore(&chip->lock, flags); | ||
778 | return change; | ||
779 | } | ||
780 | |||
781 | #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ | ||
782 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ | ||
783 | .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ | ||
784 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
785 | |||
786 | static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
787 | { | ||
788 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
789 | |||
790 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
791 | uinfo->count = 1; | ||
792 | uinfo->value.integer.min = 0; | ||
793 | uinfo->value.integer.max = mask; | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
798 | { | ||
799 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
800 | unsigned long flags; | ||
801 | int reg = kcontrol->private_value & 0xff; | ||
802 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
803 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
804 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
805 | |||
806 | spin_lock_irqsave(&chip->lock, flags); | ||
807 | ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; | ||
808 | spin_unlock_irqrestore(&chip->lock, flags); | ||
809 | if (invert) | ||
810 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
815 | { | ||
816 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
817 | unsigned long flags; | ||
818 | int reg = kcontrol->private_value & 0xff; | ||
819 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
820 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
821 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
822 | int change; | ||
823 | unsigned short old_val, val; | ||
824 | |||
825 | val = (ucontrol->value.integer.value[0] & mask); | ||
826 | if (invert) | ||
827 | val = mask - val; | ||
828 | val <<= shift; | ||
829 | spin_lock_irqsave(&chip->lock, flags); | ||
830 | old_val = snd_ad1816a_read(chip, reg); | ||
831 | val = (old_val & ~(mask << shift)) | val; | ||
832 | change = val != old_val; | ||
833 | snd_ad1816a_write(chip, reg, val); | ||
834 | spin_unlock_irqrestore(&chip->lock, flags); | ||
835 | return change; | ||
836 | } | ||
837 | |||
838 | #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ | ||
839 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ | ||
840 | .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ | ||
841 | .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } | ||
842 | |||
843 | static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
844 | { | ||
845 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
846 | |||
847 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
848 | uinfo->count = 2; | ||
849 | uinfo->value.integer.min = 0; | ||
850 | uinfo->value.integer.max = mask; | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
855 | { | ||
856 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
857 | unsigned long flags; | ||
858 | int reg = kcontrol->private_value & 0xff; | ||
859 | int shift_left = (kcontrol->private_value >> 8) & 0x0f; | ||
860 | int shift_right = (kcontrol->private_value >> 12) & 0x0f; | ||
861 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
862 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
863 | unsigned short val; | ||
864 | |||
865 | spin_lock_irqsave(&chip->lock, flags); | ||
866 | val = snd_ad1816a_read(chip, reg); | ||
867 | ucontrol->value.integer.value[0] = (val >> shift_left) & mask; | ||
868 | ucontrol->value.integer.value[1] = (val >> shift_right) & mask; | ||
869 | spin_unlock_irqrestore(&chip->lock, flags); | ||
870 | if (invert) { | ||
871 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
872 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
873 | } | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
878 | { | ||
879 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
880 | unsigned long flags; | ||
881 | int reg = kcontrol->private_value & 0xff; | ||
882 | int shift_left = (kcontrol->private_value >> 8) & 0x0f; | ||
883 | int shift_right = (kcontrol->private_value >> 12) & 0x0f; | ||
884 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
885 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
886 | int change; | ||
887 | unsigned short old_val, val1, val2; | ||
888 | |||
889 | val1 = ucontrol->value.integer.value[0] & mask; | ||
890 | val2 = ucontrol->value.integer.value[1] & mask; | ||
891 | if (invert) { | ||
892 | val1 = mask - val1; | ||
893 | val2 = mask - val2; | ||
894 | } | ||
895 | val1 <<= shift_left; | ||
896 | val2 <<= shift_right; | ||
897 | spin_lock_irqsave(&chip->lock, flags); | ||
898 | old_val = snd_ad1816a_read(chip, reg); | ||
899 | val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
900 | change = val1 != old_val; | ||
901 | snd_ad1816a_write(chip, reg, val1); | ||
902 | spin_unlock_irqrestore(&chip->lock, flags); | ||
903 | return change; | ||
904 | } | ||
905 | |||
906 | static snd_kcontrol_new_t snd_ad1816a_controls[] = { | ||
907 | AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), | ||
908 | AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), | ||
909 | AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), | ||
910 | AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), | ||
911 | AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), | ||
912 | AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), | ||
913 | AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), | ||
914 | AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), | ||
915 | AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), | ||
916 | AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), | ||
917 | AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), | ||
918 | AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), | ||
919 | AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), | ||
920 | AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1), | ||
921 | AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), | ||
922 | AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), | ||
923 | AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), | ||
924 | AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), | ||
925 | AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), | ||
926 | AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), | ||
927 | AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), | ||
928 | { | ||
929 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
930 | .name = "Capture Source", | ||
931 | .info = snd_ad1816a_info_mux, | ||
932 | .get = snd_ad1816a_get_mux, | ||
933 | .put = snd_ad1816a_put_mux, | ||
934 | }, | ||
935 | AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), | ||
936 | AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), | ||
937 | AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), | ||
938 | AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), | ||
939 | }; | ||
940 | |||
941 | int snd_ad1816a_mixer(ad1816a_t *chip) | ||
942 | { | ||
943 | snd_card_t *card; | ||
944 | unsigned int idx; | ||
945 | int err; | ||
946 | |||
947 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
948 | |||
949 | card = chip->card; | ||
950 | |||
951 | strcpy(card->mixername, snd_ad1816a_chip_id(chip)); | ||
952 | |||
953 | for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { | ||
954 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) | ||
955 | return err; | ||
956 | } | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | EXPORT_SYMBOL(snd_ad1816a_create); | ||
961 | EXPORT_SYMBOL(snd_ad1816a_pcm); | ||
962 | EXPORT_SYMBOL(snd_ad1816a_mixer); | ||
963 | |||
964 | static int __init alsa_ad1816a_init(void) | ||
965 | { | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static void __exit alsa_ad1816a_exit(void) | ||
970 | { | ||
971 | } | ||
972 | |||
973 | module_init(alsa_ad1816a_init) | ||
974 | module_exit(alsa_ad1816a_exit) | ||
diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile new file mode 100644 index 000000000000..45d59998aa69 --- /dev/null +++ b/sound/isa/ad1848/Makefile | |||
@@ -0,0 +1,15 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-ad1848-lib-objs := ad1848_lib.o | ||
7 | snd-ad1848-objs := ad1848.o | ||
8 | |||
9 | # Toplevel Module Dependency | ||
10 | obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o | ||
11 | obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o | ||
12 | obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o | ||
13 | obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o | ||
14 | |||
15 | obj-m := $(sort $(obj-m)) | ||
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c new file mode 100644 index 000000000000..8c399340cd72 --- /dev/null +++ b/sound/isa/ad1848/ad1848.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha) | ||
3 | * Copyright (c) by Tugrul Galatali <galatalt@stuy.edu>, | ||
4 | * Jaroslav Kysela <perex@suse.cz> | ||
5 | * Based on card-4232.c by Jaroslav Kysela <perex@suse.cz> | ||
6 | * | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/ad1848.h> | ||
31 | #include <sound/initval.h> | ||
32 | |||
33 | MODULE_AUTHOR("Tugrul Galatali <galatalt@stuy.edu>, Jaroslav Kysela <perex@suse.cz>"); | ||
34 | MODULE_DESCRIPTION("AD1848/AD1847/CS4248"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | MODULE_SUPPORTED_DEVICE("{{Analog Devices,AD1848}," | ||
37 | "{Analog Devices,AD1847}," | ||
38 | "{Crystal Semiconductors,CS4248}}"); | ||
39 | |||
40 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
41 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
42 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
43 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
44 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ | ||
45 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
46 | static int thinkpad[SNDRV_CARDS]; /* Thinkpad special case */ | ||
47 | |||
48 | module_param_array(index, int, NULL, 0444); | ||
49 | MODULE_PARM_DESC(index, "Index value for AD1848 soundcard."); | ||
50 | module_param_array(id, charp, NULL, 0444); | ||
51 | MODULE_PARM_DESC(id, "ID string for AD1848 soundcard."); | ||
52 | module_param_array(enable, bool, NULL, 0444); | ||
53 | MODULE_PARM_DESC(enable, "Enable AD1848 soundcard."); | ||
54 | module_param_array(port, long, NULL, 0444); | ||
55 | MODULE_PARM_DESC(port, "Port # for AD1848 driver."); | ||
56 | module_param_array(irq, int, NULL, 0444); | ||
57 | MODULE_PARM_DESC(irq, "IRQ # for AD1848 driver."); | ||
58 | module_param_array(dma1, int, NULL, 0444); | ||
59 | MODULE_PARM_DESC(dma1, "DMA1 # for AD1848 driver."); | ||
60 | module_param_array(thinkpad, bool, NULL, 0444); | ||
61 | MODULE_PARM_DESC(thinkpad, "Enable only for the onboard CS4248 of IBM Thinkpad 360/750/755 series."); | ||
62 | |||
63 | static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
64 | |||
65 | |||
66 | static int __init snd_card_ad1848_probe(int dev) | ||
67 | { | ||
68 | snd_card_t *card; | ||
69 | ad1848_t *chip; | ||
70 | snd_pcm_t *pcm; | ||
71 | int err; | ||
72 | |||
73 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
74 | snd_printk(KERN_ERR "ad1848: specify port\n"); | ||
75 | return -EINVAL; | ||
76 | } | ||
77 | if (irq[dev] == SNDRV_AUTO_IRQ) { | ||
78 | snd_printk(KERN_ERR "ad1848: specify irq\n"); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | if (dma1[dev] == SNDRV_AUTO_DMA) { | ||
82 | snd_printk(KERN_ERR "ad1848: specify dma1\n"); | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | |||
86 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
87 | if (card == NULL) | ||
88 | return -ENOMEM; | ||
89 | |||
90 | if ((err = snd_ad1848_create(card, port[dev], | ||
91 | irq[dev], | ||
92 | dma1[dev], | ||
93 | thinkpad[dev] ? AD1848_HW_THINKPAD : AD1848_HW_DETECT, | ||
94 | &chip)) < 0) { | ||
95 | snd_card_free(card); | ||
96 | return err; | ||
97 | } | ||
98 | |||
99 | if ((err = snd_ad1848_pcm(chip, 0, &pcm)) < 0) { | ||
100 | snd_card_free(card); | ||
101 | return err; | ||
102 | } | ||
103 | if ((err = snd_ad1848_mixer(chip)) < 0) { | ||
104 | snd_card_free(card); | ||
105 | return err; | ||
106 | } | ||
107 | strcpy(card->driver, "AD1848"); | ||
108 | strcpy(card->shortname, pcm->name); | ||
109 | |||
110 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||
111 | pcm->name, chip->port, irq[dev], dma1[dev]); | ||
112 | |||
113 | if (thinkpad[dev]) { | ||
114 | strcat(card->longname, " [Thinkpad]"); | ||
115 | } | ||
116 | |||
117 | if ((err = snd_card_register(card)) < 0) { | ||
118 | snd_card_free(card); | ||
119 | return err; | ||
120 | } | ||
121 | snd_ad1848_cards[dev] = card; | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int __init alsa_card_ad1848_init(void) | ||
126 | { | ||
127 | int dev, cards; | ||
128 | |||
129 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) | ||
130 | if (snd_card_ad1848_probe(dev) >= 0) | ||
131 | cards++; | ||
132 | |||
133 | if (!cards) { | ||
134 | #ifdef MODULE | ||
135 | printk(KERN_ERR "AD1848 soundcard not found or device busy\n"); | ||
136 | #endif | ||
137 | return -ENODEV; | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static void __exit alsa_card_ad1848_exit(void) | ||
143 | { | ||
144 | int idx; | ||
145 | |||
146 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
147 | snd_card_free(snd_ad1848_cards[idx]); | ||
148 | } | ||
149 | |||
150 | module_init(alsa_card_ad1848_init) | ||
151 | module_exit(alsa_card_ad1848_exit) | ||
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c new file mode 100644 index 000000000000..8fb3db103e48 --- /dev/null +++ b/sound/isa/ad1848/ad1848_lib.c | |||
@@ -0,0 +1,1279 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of AD1848/AD1847/CS4248 | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #define SNDRV_MAIN_OBJECT_FILE | ||
23 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/pm.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/ad1848.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/pcm_params.h> | ||
34 | |||
35 | #include <asm/io.h> | ||
36 | #include <asm/dma.h> | ||
37 | |||
38 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
39 | MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | #if 0 | ||
43 | #define SNDRV_DEBUG_MCE | ||
44 | #endif | ||
45 | |||
46 | /* | ||
47 | * Some variables | ||
48 | */ | ||
49 | |||
50 | static unsigned char freq_bits[14] = { | ||
51 | /* 5510 */ 0x00 | AD1848_XTAL2, | ||
52 | /* 6620 */ 0x0E | AD1848_XTAL2, | ||
53 | /* 8000 */ 0x00 | AD1848_XTAL1, | ||
54 | /* 9600 */ 0x0E | AD1848_XTAL1, | ||
55 | /* 11025 */ 0x02 | AD1848_XTAL2, | ||
56 | /* 16000 */ 0x02 | AD1848_XTAL1, | ||
57 | /* 18900 */ 0x04 | AD1848_XTAL2, | ||
58 | /* 22050 */ 0x06 | AD1848_XTAL2, | ||
59 | /* 27042 */ 0x04 | AD1848_XTAL1, | ||
60 | /* 32000 */ 0x06 | AD1848_XTAL1, | ||
61 | /* 33075 */ 0x0C | AD1848_XTAL2, | ||
62 | /* 37800 */ 0x08 | AD1848_XTAL2, | ||
63 | /* 44100 */ 0x0A | AD1848_XTAL2, | ||
64 | /* 48000 */ 0x0C | AD1848_XTAL1 | ||
65 | }; | ||
66 | |||
67 | static unsigned int rates[14] = { | ||
68 | 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, | ||
69 | 27042, 32000, 33075, 37800, 44100, 48000 | ||
70 | }; | ||
71 | |||
72 | static snd_pcm_hw_constraint_list_t hw_constraints_rates = { | ||
73 | .count = 14, | ||
74 | .list = rates, | ||
75 | .mask = 0, | ||
76 | }; | ||
77 | |||
78 | static unsigned char snd_ad1848_original_image[16] = | ||
79 | { | ||
80 | 0x00, /* 00 - lic */ | ||
81 | 0x00, /* 01 - ric */ | ||
82 | 0x9f, /* 02 - la1ic */ | ||
83 | 0x9f, /* 03 - ra1ic */ | ||
84 | 0x9f, /* 04 - la2ic */ | ||
85 | 0x9f, /* 05 - ra2ic */ | ||
86 | 0xbf, /* 06 - loc */ | ||
87 | 0xbf, /* 07 - roc */ | ||
88 | 0x20, /* 08 - dfr */ | ||
89 | AD1848_AUTOCALIB, /* 09 - ic */ | ||
90 | 0x00, /* 0a - pc */ | ||
91 | 0x00, /* 0b - ti */ | ||
92 | 0x00, /* 0c - mi */ | ||
93 | 0x00, /* 0d - lbc */ | ||
94 | 0x00, /* 0e - dru */ | ||
95 | 0x00, /* 0f - drl */ | ||
96 | }; | ||
97 | |||
98 | /* | ||
99 | * Basic I/O functions | ||
100 | */ | ||
101 | |||
102 | void snd_ad1848_out(ad1848_t *chip, | ||
103 | unsigned char reg, | ||
104 | unsigned char value) | ||
105 | { | ||
106 | int timeout; | ||
107 | |||
108 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
109 | udelay(100); | ||
110 | #ifdef CONFIG_SND_DEBUG | ||
111 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
112 | snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); | ||
113 | #endif | ||
114 | outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); | ||
115 | outb(chip->image[reg] = value, AD1848P(chip, REG)); | ||
116 | mb(); | ||
117 | #if 0 | ||
118 | printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); | ||
119 | #endif | ||
120 | } | ||
121 | |||
122 | static void snd_ad1848_dout(ad1848_t *chip, | ||
123 | unsigned char reg, unsigned char value) | ||
124 | { | ||
125 | int timeout; | ||
126 | |||
127 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
128 | udelay(100); | ||
129 | outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); | ||
130 | outb(value, AD1848P(chip, REG)); | ||
131 | mb(); | ||
132 | } | ||
133 | |||
134 | static unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg) | ||
135 | { | ||
136 | int timeout; | ||
137 | |||
138 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
139 | udelay(100); | ||
140 | #ifdef CONFIG_SND_DEBUG | ||
141 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
142 | snd_printk("auto calibration time out - reg = 0x%x\n", reg); | ||
143 | #endif | ||
144 | outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); | ||
145 | mb(); | ||
146 | return inb(AD1848P(chip, REG)); | ||
147 | } | ||
148 | |||
149 | #if 0 | ||
150 | |||
151 | static void snd_ad1848_debug(ad1848_t *chip) | ||
152 | { | ||
153 | printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL))); | ||
154 | printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS))); | ||
155 | printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00)); | ||
156 | printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08)); | ||
157 | printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01)); | ||
158 | printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09)); | ||
159 | printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02)); | ||
160 | printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a)); | ||
161 | printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03)); | ||
162 | printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b)); | ||
163 | printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04)); | ||
164 | printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c)); | ||
165 | printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05)); | ||
166 | printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d)); | ||
167 | printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06)); | ||
168 | printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e)); | ||
169 | printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07)); | ||
170 | printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f)); | ||
171 | } | ||
172 | |||
173 | #endif | ||
174 | |||
175 | /* | ||
176 | * AD1848 detection / MCE routines | ||
177 | */ | ||
178 | |||
179 | static void snd_ad1848_mce_up(ad1848_t *chip) | ||
180 | { | ||
181 | unsigned long flags; | ||
182 | int timeout; | ||
183 | |||
184 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
185 | udelay(100); | ||
186 | #ifdef CONFIG_SND_DEBUG | ||
187 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
188 | snd_printk("mce_up - auto calibration time out (0)\n"); | ||
189 | #endif | ||
190 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
191 | chip->mce_bit |= AD1848_MCE; | ||
192 | timeout = inb(AD1848P(chip, REGSEL)); | ||
193 | if (timeout == 0x80) | ||
194 | snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
195 | if (!(timeout & AD1848_MCE)) | ||
196 | outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); | ||
197 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
198 | } | ||
199 | |||
200 | static void snd_ad1848_mce_down(ad1848_t *chip) | ||
201 | { | ||
202 | unsigned long flags; | ||
203 | int timeout; | ||
204 | signed long time; | ||
205 | |||
206 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
207 | for (timeout = 5; timeout > 0; timeout--) | ||
208 | inb(AD1848P(chip, REGSEL)); | ||
209 | /* end of cleanup sequence */ | ||
210 | for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
211 | udelay(100); | ||
212 | #if 0 | ||
213 | printk("(1) timeout = %i\n", timeout); | ||
214 | #endif | ||
215 | #ifdef CONFIG_SND_DEBUG | ||
216 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
217 | snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL)); | ||
218 | #endif | ||
219 | chip->mce_bit &= ~AD1848_MCE; | ||
220 | timeout = inb(AD1848P(chip, REGSEL)); | ||
221 | outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); | ||
222 | if (timeout == 0x80) | ||
223 | snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
224 | if ((timeout & AD1848_MCE) == 0) { | ||
225 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
226 | return; | ||
227 | } | ||
228 | /* calibration process */ | ||
229 | |||
230 | for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--); | ||
231 | if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) { | ||
232 | snd_printd("mce_down - auto calibration time out (1)\n"); | ||
233 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
234 | return; | ||
235 | } | ||
236 | #if 0 | ||
237 | printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); | ||
238 | #endif | ||
239 | time = HZ / 4; | ||
240 | while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) { | ||
241 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
242 | if (time <= 0) { | ||
243 | snd_printk("mce_down - auto calibration time out (2)\n"); | ||
244 | return; | ||
245 | } | ||
246 | set_current_state(TASK_INTERRUPTIBLE); | ||
247 | time = schedule_timeout(time); | ||
248 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
249 | } | ||
250 | #if 0 | ||
251 | printk("(3) jiffies = %li\n", jiffies); | ||
252 | #endif | ||
253 | time = HZ / 10; | ||
254 | while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) { | ||
255 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
256 | if (time <= 0) { | ||
257 | snd_printk("mce_down - auto calibration time out (3)\n"); | ||
258 | return; | ||
259 | } | ||
260 | set_current_state(TASK_INTERRUPTIBLE); | ||
261 | time = schedule_timeout(time); | ||
262 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
263 | } | ||
264 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
265 | #if 0 | ||
266 | printk("(4) jiffies = %li\n", jiffies); | ||
267 | snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL))); | ||
268 | #endif | ||
269 | } | ||
270 | |||
271 | static unsigned int snd_ad1848_get_count(unsigned char format, | ||
272 | unsigned int size) | ||
273 | { | ||
274 | switch (format & 0xe0) { | ||
275 | case AD1848_LINEAR_16: | ||
276 | size >>= 1; | ||
277 | break; | ||
278 | } | ||
279 | if (format & AD1848_STEREO) | ||
280 | size >>= 1; | ||
281 | return size; | ||
282 | } | ||
283 | |||
284 | static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what, | ||
285 | int channel, int cmd) | ||
286 | { | ||
287 | int result = 0; | ||
288 | |||
289 | #if 0 | ||
290 | printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS))); | ||
291 | #endif | ||
292 | spin_lock(&chip->reg_lock); | ||
293 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
294 | if (chip->image[AD1848_IFACE_CTRL] & what) { | ||
295 | spin_unlock(&chip->reg_lock); | ||
296 | return 0; | ||
297 | } | ||
298 | snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what); | ||
299 | chip->mode |= AD1848_MODE_RUNNING; | ||
300 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
301 | if (!(chip->image[AD1848_IFACE_CTRL] & what)) { | ||
302 | spin_unlock(&chip->reg_lock); | ||
303 | return 0; | ||
304 | } | ||
305 | snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what); | ||
306 | chip->mode &= ~AD1848_MODE_RUNNING; | ||
307 | } else { | ||
308 | result = -EINVAL; | ||
309 | } | ||
310 | spin_unlock(&chip->reg_lock); | ||
311 | return result; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * CODEC I/O | ||
316 | */ | ||
317 | |||
318 | static unsigned char snd_ad1848_get_rate(unsigned int rate) | ||
319 | { | ||
320 | int i; | ||
321 | |||
322 | for (i = 0; i < 14; i++) | ||
323 | if (rate == rates[i]) | ||
324 | return freq_bits[i]; | ||
325 | snd_BUG(); | ||
326 | return freq_bits[13]; | ||
327 | } | ||
328 | |||
329 | static int snd_ad1848_ioctl(snd_pcm_substream_t * substream, | ||
330 | unsigned int cmd, void *arg) | ||
331 | { | ||
332 | return snd_pcm_lib_ioctl(substream, cmd, arg); | ||
333 | } | ||
334 | |||
335 | static unsigned char snd_ad1848_get_format(int format, int channels) | ||
336 | { | ||
337 | unsigned char rformat; | ||
338 | |||
339 | rformat = AD1848_LINEAR_8; | ||
340 | switch (format) { | ||
341 | case SNDRV_PCM_FORMAT_A_LAW: rformat = AD1848_ALAW_8; break; | ||
342 | case SNDRV_PCM_FORMAT_MU_LAW: rformat = AD1848_ULAW_8; break; | ||
343 | case SNDRV_PCM_FORMAT_S16_LE: rformat = AD1848_LINEAR_16; break; | ||
344 | } | ||
345 | if (channels > 1) | ||
346 | rformat |= AD1848_STEREO; | ||
347 | #if 0 | ||
348 | snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); | ||
349 | #endif | ||
350 | return rformat; | ||
351 | } | ||
352 | |||
353 | static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute) | ||
354 | { | ||
355 | unsigned long flags; | ||
356 | |||
357 | mute = mute ? 1 : 0; | ||
358 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
359 | if (chip->calibrate_mute == mute) { | ||
360 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
361 | return; | ||
362 | } | ||
363 | if (!mute) { | ||
364 | snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]); | ||
365 | snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]); | ||
366 | } | ||
367 | snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]); | ||
368 | snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]); | ||
369 | snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]); | ||
370 | snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]); | ||
371 | snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]); | ||
372 | snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]); | ||
373 | chip->calibrate_mute = mute; | ||
374 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
375 | } | ||
376 | |||
377 | static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params) | ||
378 | { | ||
379 | if (hw_params == NULL) { | ||
380 | chip->image[AD1848_DATA_FORMAT] = 0x20; | ||
381 | } else { | ||
382 | chip->image[AD1848_DATA_FORMAT] = | ||
383 | snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) | | ||
384 | snd_ad1848_get_rate(params_rate(hw_params)); | ||
385 | } | ||
386 | // snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]); | ||
387 | } | ||
388 | |||
389 | static int snd_ad1848_open(ad1848_t *chip, unsigned int mode) | ||
390 | { | ||
391 | unsigned long flags; | ||
392 | |||
393 | down(&chip->open_mutex); | ||
394 | if (chip->mode & AD1848_MODE_OPEN) { | ||
395 | up(&chip->open_mutex); | ||
396 | return -EAGAIN; | ||
397 | } | ||
398 | snd_ad1848_mce_down(chip); | ||
399 | |||
400 | #ifdef SNDRV_DEBUG_MCE | ||
401 | snd_printk("open: (1)\n"); | ||
402 | #endif | ||
403 | snd_ad1848_mce_up(chip); | ||
404 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
405 | chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | | ||
406 | AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO | | ||
407 | AD1848_CALIB_MODE); | ||
408 | chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB; | ||
409 | snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); | ||
410 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
411 | snd_ad1848_mce_down(chip); | ||
412 | |||
413 | #ifdef SNDRV_DEBUG_MCE | ||
414 | snd_printk("open: (2)\n"); | ||
415 | #endif | ||
416 | |||
417 | snd_ad1848_set_data_format(chip, NULL); | ||
418 | |||
419 | snd_ad1848_mce_up(chip); | ||
420 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
421 | snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); | ||
422 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
423 | snd_ad1848_mce_down(chip); | ||
424 | |||
425 | #ifdef SNDRV_DEBUG_MCE | ||
426 | snd_printk("open: (3)\n"); | ||
427 | #endif | ||
428 | |||
429 | /* ok. now enable and ack CODEC IRQ */ | ||
430 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
431 | outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ | ||
432 | outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ | ||
433 | chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE; | ||
434 | snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); | ||
435 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
436 | |||
437 | chip->mode = mode; | ||
438 | up(&chip->open_mutex); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static void snd_ad1848_close(ad1848_t *chip) | ||
444 | { | ||
445 | unsigned long flags; | ||
446 | |||
447 | down(&chip->open_mutex); | ||
448 | if (!chip->mode) { | ||
449 | up(&chip->open_mutex); | ||
450 | return; | ||
451 | } | ||
452 | /* disable IRQ */ | ||
453 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
454 | outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ | ||
455 | outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ | ||
456 | chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE; | ||
457 | snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); | ||
458 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
459 | |||
460 | /* now disable capture & playback */ | ||
461 | |||
462 | snd_ad1848_mce_up(chip); | ||
463 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
464 | chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | | ||
465 | AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); | ||
466 | snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); | ||
467 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
468 | snd_ad1848_mce_down(chip); | ||
469 | |||
470 | /* clear IRQ again */ | ||
471 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
472 | outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ | ||
473 | outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ | ||
474 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
475 | |||
476 | chip->mode = 0; | ||
477 | up(&chip->open_mutex); | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * ok.. exported functions.. | ||
482 | */ | ||
483 | |||
484 | static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream, | ||
485 | int cmd) | ||
486 | { | ||
487 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
488 | return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd); | ||
489 | } | ||
490 | |||
491 | static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream, | ||
492 | int cmd) | ||
493 | { | ||
494 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
495 | return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd); | ||
496 | } | ||
497 | |||
498 | static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream, | ||
499 | snd_pcm_hw_params_t * hw_params) | ||
500 | { | ||
501 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
502 | unsigned long flags; | ||
503 | int err; | ||
504 | |||
505 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
506 | return err; | ||
507 | snd_ad1848_calibrate_mute(chip, 1); | ||
508 | snd_ad1848_set_data_format(chip, hw_params); | ||
509 | snd_ad1848_mce_up(chip); | ||
510 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
511 | snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); | ||
512 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
513 | snd_ad1848_mce_down(chip); | ||
514 | snd_ad1848_calibrate_mute(chip, 0); | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream) | ||
519 | { | ||
520 | return snd_pcm_lib_free_pages(substream); | ||
521 | } | ||
522 | |||
523 | static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream) | ||
524 | { | ||
525 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
526 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
527 | unsigned long flags; | ||
528 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
529 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
530 | |||
531 | chip->dma_size = size; | ||
532 | chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO); | ||
533 | snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
534 | count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; | ||
535 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
536 | snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); | ||
537 | snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); | ||
538 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream, | ||
543 | snd_pcm_hw_params_t * hw_params) | ||
544 | { | ||
545 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
546 | unsigned long flags; | ||
547 | int err; | ||
548 | |||
549 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
550 | return err; | ||
551 | snd_ad1848_calibrate_mute(chip, 1); | ||
552 | snd_ad1848_set_data_format(chip, hw_params); | ||
553 | snd_ad1848_mce_up(chip); | ||
554 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
555 | snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); | ||
556 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
557 | snd_ad1848_mce_down(chip); | ||
558 | snd_ad1848_calibrate_mute(chip, 0); | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream) | ||
563 | { | ||
564 | return snd_pcm_lib_free_pages(substream); | ||
565 | } | ||
566 | |||
567 | static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream) | ||
568 | { | ||
569 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
570 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
571 | unsigned long flags; | ||
572 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
573 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
574 | |||
575 | chip->dma_size = size; | ||
576 | chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); | ||
577 | snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | ||
578 | count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; | ||
579 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
580 | snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); | ||
581 | snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); | ||
582 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
587 | { | ||
588 | ad1848_t *chip = dev_id; | ||
589 | |||
590 | if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream && | ||
591 | (chip->mode & AD1848_MODE_RUNNING)) | ||
592 | snd_pcm_period_elapsed(chip->playback_substream); | ||
593 | if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream && | ||
594 | (chip->mode & AD1848_MODE_RUNNING)) | ||
595 | snd_pcm_period_elapsed(chip->capture_substream); | ||
596 | outb(0, AD1848P(chip, STATUS)); /* clear global interrupt bit */ | ||
597 | return IRQ_HANDLED; | ||
598 | } | ||
599 | |||
600 | static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream) | ||
601 | { | ||
602 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
603 | size_t ptr; | ||
604 | |||
605 | if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE)) | ||
606 | return 0; | ||
607 | ptr = snd_dma_pointer(chip->dma, chip->dma_size); | ||
608 | return bytes_to_frames(substream->runtime, ptr); | ||
609 | } | ||
610 | |||
611 | static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream) | ||
612 | { | ||
613 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
614 | size_t ptr; | ||
615 | |||
616 | if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE)) | ||
617 | return 0; | ||
618 | ptr = snd_dma_pointer(chip->dma, chip->dma_size); | ||
619 | return bytes_to_frames(substream->runtime, ptr); | ||
620 | } | ||
621 | |||
622 | /* | ||
623 | |||
624 | */ | ||
625 | |||
626 | static void snd_ad1848_thinkpad_twiddle(ad1848_t *chip, int on) { | ||
627 | |||
628 | int tmp; | ||
629 | |||
630 | if (!chip->thinkpad_flag) return; | ||
631 | |||
632 | outb(0x1c, AD1848_THINKPAD_CTL_PORT1); | ||
633 | tmp = inb(AD1848_THINKPAD_CTL_PORT2); | ||
634 | |||
635 | if (on) | ||
636 | /* turn it on */ | ||
637 | tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT; | ||
638 | else | ||
639 | /* turn it off */ | ||
640 | tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT; | ||
641 | |||
642 | outb(tmp, AD1848_THINKPAD_CTL_PORT2); | ||
643 | |||
644 | } | ||
645 | |||
646 | #ifdef CONFIG_PM | ||
647 | static int snd_ad1848_suspend(snd_card_t *card, pm_message_t state) | ||
648 | { | ||
649 | ad1848_t *chip = card->pm_private_data; | ||
650 | |||
651 | snd_pcm_suspend_all(chip->pcm); | ||
652 | /* FIXME: save registers? */ | ||
653 | |||
654 | if (chip->thinkpad_flag) | ||
655 | snd_ad1848_thinkpad_twiddle(chip, 0); | ||
656 | |||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static int snd_ad1848_resume(snd_card_t *card) | ||
661 | { | ||
662 | ad1848_t *chip = card->pm_private_data; | ||
663 | |||
664 | if (chip->thinkpad_flag) | ||
665 | snd_ad1848_thinkpad_twiddle(chip, 1); | ||
666 | |||
667 | /* FIXME: restore registers? */ | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | #endif /* CONFIG_PM */ | ||
672 | |||
673 | static int snd_ad1848_probe(ad1848_t * chip) | ||
674 | { | ||
675 | unsigned long flags; | ||
676 | int i, id, rev, ad1847; | ||
677 | unsigned char *ptr; | ||
678 | |||
679 | #if 0 | ||
680 | snd_ad1848_debug(chip); | ||
681 | #endif | ||
682 | id = ad1847 = 0; | ||
683 | for (i = 0; i < 1000; i++) { | ||
684 | mb(); | ||
685 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
686 | udelay(500); | ||
687 | else { | ||
688 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
689 | snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); | ||
690 | snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa); | ||
691 | snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45); | ||
692 | rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT); | ||
693 | if (rev == 0x65) { | ||
694 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
695 | id = 1; | ||
696 | ad1847 = 1; | ||
697 | break; | ||
698 | } | ||
699 | if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) { | ||
700 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
701 | id = 1; | ||
702 | break; | ||
703 | } | ||
704 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
705 | } | ||
706 | } | ||
707 | if (id != 1) | ||
708 | return -ENODEV; /* no valid device found */ | ||
709 | if (chip->hardware == AD1848_HW_DETECT) { | ||
710 | if (ad1847) { | ||
711 | chip->hardware = AD1848_HW_AD1847; | ||
712 | } else { | ||
713 | chip->hardware = AD1848_HW_AD1848; | ||
714 | rev = snd_ad1848_in(chip, AD1848_MISC_INFO); | ||
715 | if (rev & 0x80) { | ||
716 | chip->hardware = AD1848_HW_CS4248; | ||
717 | } else if ((rev & 0x0f) == 0x0a) { | ||
718 | snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40); | ||
719 | for (i = 0; i < 16; ++i) { | ||
720 | if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) { | ||
721 | chip->hardware = AD1848_HW_CMI8330; | ||
722 | break; | ||
723 | } | ||
724 | } | ||
725 | snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
730 | inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */ | ||
731 | outb(0, AD1848P(chip, STATUS)); | ||
732 | mb(); | ||
733 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
734 | |||
735 | chip->image[AD1848_MISC_INFO] = 0x00; | ||
736 | chip->image[AD1848_IFACE_CTRL] = | ||
737 | (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA; | ||
738 | ptr = (unsigned char *) &chip->image; | ||
739 | snd_ad1848_mce_down(chip); | ||
740 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
741 | for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */ | ||
742 | snd_ad1848_out(chip, i, *ptr++); | ||
743 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
744 | snd_ad1848_mce_up(chip); | ||
745 | snd_ad1848_mce_down(chip); | ||
746 | return 0; /* all things are ok.. */ | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | |||
751 | */ | ||
752 | |||
753 | static snd_pcm_hardware_t snd_ad1848_playback = | ||
754 | { | ||
755 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
756 | SNDRV_PCM_INFO_MMAP_VALID), | ||
757 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
758 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), | ||
759 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
760 | .rate_min = 5510, | ||
761 | .rate_max = 48000, | ||
762 | .channels_min = 1, | ||
763 | .channels_max = 2, | ||
764 | .buffer_bytes_max = (128*1024), | ||
765 | .period_bytes_min = 64, | ||
766 | .period_bytes_max = (128*1024), | ||
767 | .periods_min = 1, | ||
768 | .periods_max = 1024, | ||
769 | .fifo_size = 0, | ||
770 | }; | ||
771 | |||
772 | static snd_pcm_hardware_t snd_ad1848_capture = | ||
773 | { | ||
774 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
775 | SNDRV_PCM_INFO_MMAP_VALID), | ||
776 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
777 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), | ||
778 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
779 | .rate_min = 5510, | ||
780 | .rate_max = 48000, | ||
781 | .channels_min = 1, | ||
782 | .channels_max = 2, | ||
783 | .buffer_bytes_max = (128*1024), | ||
784 | .period_bytes_min = 64, | ||
785 | .period_bytes_max = (128*1024), | ||
786 | .periods_min = 1, | ||
787 | .periods_max = 1024, | ||
788 | .fifo_size = 0, | ||
789 | }; | ||
790 | |||
791 | /* | ||
792 | |||
793 | */ | ||
794 | |||
795 | static int snd_ad1848_playback_open(snd_pcm_substream_t * substream) | ||
796 | { | ||
797 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
798 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
799 | int err; | ||
800 | |||
801 | if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0) | ||
802 | return err; | ||
803 | chip->playback_substream = substream; | ||
804 | runtime->hw = snd_ad1848_playback; | ||
805 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); | ||
806 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); | ||
807 | snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static int snd_ad1848_capture_open(snd_pcm_substream_t * substream) | ||
812 | { | ||
813 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
814 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
815 | int err; | ||
816 | |||
817 | if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0) | ||
818 | return err; | ||
819 | chip->capture_substream = substream; | ||
820 | runtime->hw = snd_ad1848_capture; | ||
821 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); | ||
822 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); | ||
823 | snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | static int snd_ad1848_playback_close(snd_pcm_substream_t * substream) | ||
828 | { | ||
829 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
830 | |||
831 | chip->mode &= ~AD1848_MODE_PLAY; | ||
832 | chip->playback_substream = NULL; | ||
833 | snd_ad1848_close(chip); | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | static int snd_ad1848_capture_close(snd_pcm_substream_t * substream) | ||
838 | { | ||
839 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
840 | |||
841 | chip->mode &= ~AD1848_MODE_CAPTURE; | ||
842 | chip->capture_substream = NULL; | ||
843 | snd_ad1848_close(chip); | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | static int snd_ad1848_free(ad1848_t *chip) | ||
848 | { | ||
849 | if (chip->res_port) { | ||
850 | release_resource(chip->res_port); | ||
851 | kfree_nocheck(chip->res_port); | ||
852 | } | ||
853 | if (chip->irq >= 0) | ||
854 | free_irq(chip->irq, (void *) chip); | ||
855 | if (chip->dma >= 0) { | ||
856 | snd_dma_disable(chip->dma); | ||
857 | free_dma(chip->dma); | ||
858 | } | ||
859 | kfree(chip); | ||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int snd_ad1848_dev_free(snd_device_t *device) | ||
864 | { | ||
865 | ad1848_t *chip = device->device_data; | ||
866 | return snd_ad1848_free(chip); | ||
867 | } | ||
868 | |||
869 | static const char *snd_ad1848_chip_id(ad1848_t *chip) | ||
870 | { | ||
871 | switch (chip->hardware) { | ||
872 | case AD1848_HW_AD1847: return "AD1847"; | ||
873 | case AD1848_HW_AD1848: return "AD1848"; | ||
874 | case AD1848_HW_CS4248: return "CS4248"; | ||
875 | case AD1848_HW_CMI8330: return "CMI8330/C3D"; | ||
876 | default: return "???"; | ||
877 | } | ||
878 | } | ||
879 | |||
880 | int snd_ad1848_create(snd_card_t * card, | ||
881 | unsigned long port, | ||
882 | int irq, int dma, | ||
883 | unsigned short hardware, | ||
884 | ad1848_t ** rchip) | ||
885 | { | ||
886 | static snd_device_ops_t ops = { | ||
887 | .dev_free = snd_ad1848_dev_free, | ||
888 | }; | ||
889 | ad1848_t *chip; | ||
890 | int err; | ||
891 | |||
892 | *rchip = NULL; | ||
893 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
894 | if (chip == NULL) | ||
895 | return -ENOMEM; | ||
896 | spin_lock_init(&chip->reg_lock); | ||
897 | init_MUTEX(&chip->open_mutex); | ||
898 | chip->card = card; | ||
899 | chip->port = port; | ||
900 | chip->irq = -1; | ||
901 | chip->dma = -1; | ||
902 | chip->hardware = hardware; | ||
903 | memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image)); | ||
904 | |||
905 | if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) { | ||
906 | snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port); | ||
907 | snd_ad1848_free(chip); | ||
908 | return -EBUSY; | ||
909 | } | ||
910 | if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) { | ||
911 | snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq); | ||
912 | snd_ad1848_free(chip); | ||
913 | return -EBUSY; | ||
914 | } | ||
915 | chip->irq = irq; | ||
916 | if (request_dma(dma, "AD1848")) { | ||
917 | snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma); | ||
918 | snd_ad1848_free(chip); | ||
919 | return -EBUSY; | ||
920 | } | ||
921 | chip->dma = dma; | ||
922 | |||
923 | if (hardware == AD1848_HW_THINKPAD) { | ||
924 | chip->thinkpad_flag = 1; | ||
925 | chip->hardware = AD1848_HW_DETECT; /* reset */ | ||
926 | snd_ad1848_thinkpad_twiddle(chip, 1); | ||
927 | snd_card_set_isa_pm_callback(card, snd_ad1848_suspend, snd_ad1848_resume, chip); | ||
928 | } | ||
929 | |||
930 | if (snd_ad1848_probe(chip) < 0) { | ||
931 | snd_ad1848_free(chip); | ||
932 | return -ENODEV; | ||
933 | } | ||
934 | |||
935 | /* Register device */ | ||
936 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
937 | snd_ad1848_free(chip); | ||
938 | return err; | ||
939 | } | ||
940 | |||
941 | *rchip = chip; | ||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | static snd_pcm_ops_t snd_ad1848_playback_ops = { | ||
946 | .open = snd_ad1848_playback_open, | ||
947 | .close = snd_ad1848_playback_close, | ||
948 | .ioctl = snd_ad1848_ioctl, | ||
949 | .hw_params = snd_ad1848_playback_hw_params, | ||
950 | .hw_free = snd_ad1848_playback_hw_free, | ||
951 | .prepare = snd_ad1848_playback_prepare, | ||
952 | .trigger = snd_ad1848_playback_trigger, | ||
953 | .pointer = snd_ad1848_playback_pointer, | ||
954 | }; | ||
955 | |||
956 | static snd_pcm_ops_t snd_ad1848_capture_ops = { | ||
957 | .open = snd_ad1848_capture_open, | ||
958 | .close = snd_ad1848_capture_close, | ||
959 | .ioctl = snd_ad1848_ioctl, | ||
960 | .hw_params = snd_ad1848_capture_hw_params, | ||
961 | .hw_free = snd_ad1848_capture_hw_free, | ||
962 | .prepare = snd_ad1848_capture_prepare, | ||
963 | .trigger = snd_ad1848_capture_trigger, | ||
964 | .pointer = snd_ad1848_capture_pointer, | ||
965 | }; | ||
966 | |||
967 | static void snd_ad1848_pcm_free(snd_pcm_t *pcm) | ||
968 | { | ||
969 | ad1848_t *chip = pcm->private_data; | ||
970 | chip->pcm = NULL; | ||
971 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
972 | } | ||
973 | |||
974 | int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) | ||
975 | { | ||
976 | snd_pcm_t *pcm; | ||
977 | int err; | ||
978 | |||
979 | if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0) | ||
980 | return err; | ||
981 | |||
982 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops); | ||
983 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops); | ||
984 | |||
985 | pcm->private_free = snd_ad1848_pcm_free; | ||
986 | pcm->private_data = chip; | ||
987 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
988 | strcpy(pcm->name, snd_ad1848_chip_id(chip)); | ||
989 | |||
990 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
991 | snd_dma_isa_data(), | ||
992 | 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); | ||
993 | |||
994 | chip->pcm = pcm; | ||
995 | if (rpcm) | ||
996 | *rpcm = pcm; | ||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction) | ||
1001 | { | ||
1002 | return direction == SNDRV_PCM_STREAM_PLAYBACK ? | ||
1003 | &snd_ad1848_playback_ops : &snd_ad1848_capture_ops; | ||
1004 | } | ||
1005 | |||
1006 | /* | ||
1007 | * MIXER part | ||
1008 | */ | ||
1009 | |||
1010 | static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1011 | { | ||
1012 | static char *texts[4] = { | ||
1013 | "Line", "Aux", "Mic", "Mix" | ||
1014 | }; | ||
1015 | |||
1016 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
1017 | uinfo->count = 2; | ||
1018 | uinfo->value.enumerated.items = 4; | ||
1019 | if (uinfo->value.enumerated.item > 3) | ||
1020 | uinfo->value.enumerated.item = 3; | ||
1021 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
1025 | static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1026 | { | ||
1027 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1028 | unsigned long flags; | ||
1029 | |||
1030 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1031 | ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6; | ||
1032 | ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6; | ||
1033 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1037 | static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1038 | { | ||
1039 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1040 | unsigned long flags; | ||
1041 | unsigned short left, right; | ||
1042 | int change; | ||
1043 | |||
1044 | if (ucontrol->value.enumerated.item[0] > 3 || | ||
1045 | ucontrol->value.enumerated.item[1] > 3) | ||
1046 | return -EINVAL; | ||
1047 | left = ucontrol->value.enumerated.item[0] << 6; | ||
1048 | right = ucontrol->value.enumerated.item[1] << 6; | ||
1049 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1050 | left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left; | ||
1051 | right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right; | ||
1052 | change = left != chip->image[AD1848_LEFT_INPUT] || | ||
1053 | right != chip->image[AD1848_RIGHT_INPUT]; | ||
1054 | snd_ad1848_out(chip, AD1848_LEFT_INPUT, left); | ||
1055 | snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right); | ||
1056 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1057 | return change; | ||
1058 | } | ||
1059 | |||
1060 | static int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1061 | { | ||
1062 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1063 | |||
1064 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1065 | uinfo->count = 1; | ||
1066 | uinfo->value.integer.min = 0; | ||
1067 | uinfo->value.integer.max = mask; | ||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | static int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1072 | { | ||
1073 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1074 | unsigned long flags; | ||
1075 | int reg = kcontrol->private_value & 0xff; | ||
1076 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1077 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1078 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1079 | |||
1080 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1081 | ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; | ||
1082 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1083 | if (invert) | ||
1084 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | static int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1089 | { | ||
1090 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1091 | unsigned long flags; | ||
1092 | int reg = kcontrol->private_value & 0xff; | ||
1093 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1094 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1095 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1096 | int change; | ||
1097 | unsigned short val; | ||
1098 | |||
1099 | val = (ucontrol->value.integer.value[0] & mask); | ||
1100 | if (invert) | ||
1101 | val = mask - val; | ||
1102 | val <<= shift; | ||
1103 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1104 | val = (chip->image[reg] & ~(mask << shift)) | val; | ||
1105 | change = val != chip->image[reg]; | ||
1106 | snd_ad1848_out(chip, reg, val); | ||
1107 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1108 | return change; | ||
1109 | } | ||
1110 | |||
1111 | static int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1112 | { | ||
1113 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1114 | |||
1115 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1116 | uinfo->count = 2; | ||
1117 | uinfo->value.integer.min = 0; | ||
1118 | uinfo->value.integer.max = mask; | ||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | static int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1123 | { | ||
1124 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1125 | unsigned long flags; | ||
1126 | int left_reg = kcontrol->private_value & 0xff; | ||
1127 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1128 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1129 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1130 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1131 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1132 | |||
1133 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1134 | ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; | ||
1135 | ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; | ||
1136 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1137 | if (invert) { | ||
1138 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1139 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
1140 | } | ||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | static int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1145 | { | ||
1146 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1147 | unsigned long flags; | ||
1148 | int left_reg = kcontrol->private_value & 0xff; | ||
1149 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1150 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1151 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1152 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1153 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1154 | int change; | ||
1155 | unsigned short val1, val2; | ||
1156 | |||
1157 | val1 = ucontrol->value.integer.value[0] & mask; | ||
1158 | val2 = ucontrol->value.integer.value[1] & mask; | ||
1159 | if (invert) { | ||
1160 | val1 = mask - val1; | ||
1161 | val2 = mask - val2; | ||
1162 | } | ||
1163 | val1 <<= shift_left; | ||
1164 | val2 <<= shift_right; | ||
1165 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1166 | if (left_reg != right_reg) { | ||
1167 | val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; | ||
1168 | val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; | ||
1169 | change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; | ||
1170 | snd_ad1848_out(chip, left_reg, val1); | ||
1171 | snd_ad1848_out(chip, right_reg, val2); | ||
1172 | } else { | ||
1173 | val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
1174 | change = val1 != chip->image[left_reg]; | ||
1175 | snd_ad1848_out(chip, left_reg, val1); | ||
1176 | } | ||
1177 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1178 | return change; | ||
1179 | } | ||
1180 | |||
1181 | /* | ||
1182 | */ | ||
1183 | int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, unsigned long value) | ||
1184 | { | ||
1185 | static snd_kcontrol_new_t newctls[] = { | ||
1186 | [AD1848_MIX_SINGLE] = { | ||
1187 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1188 | .info = snd_ad1848_info_single, | ||
1189 | .get = snd_ad1848_get_single, | ||
1190 | .put = snd_ad1848_put_single, | ||
1191 | }, | ||
1192 | [AD1848_MIX_DOUBLE] = { | ||
1193 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1194 | .info = snd_ad1848_info_double, | ||
1195 | .get = snd_ad1848_get_double, | ||
1196 | .put = snd_ad1848_put_double, | ||
1197 | }, | ||
1198 | [AD1848_MIX_CAPTURE] = { | ||
1199 | .info = snd_ad1848_info_mux, | ||
1200 | .get = snd_ad1848_get_mux, | ||
1201 | .put = snd_ad1848_put_mux, | ||
1202 | }, | ||
1203 | }; | ||
1204 | snd_kcontrol_t *ctl; | ||
1205 | int err; | ||
1206 | |||
1207 | ctl = snd_ctl_new1(&newctls[type], chip); | ||
1208 | if (! ctl) | ||
1209 | return -ENOMEM; | ||
1210 | strlcpy(ctl->id.name, name, sizeof(ctl->id.name)); | ||
1211 | ctl->id.index = index; | ||
1212 | ctl->private_value = value; | ||
1213 | if ((err = snd_ctl_add(chip->card, ctl)) < 0) { | ||
1214 | snd_ctl_free_one(ctl); | ||
1215 | return err; | ||
1216 | } | ||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | |||
1221 | static struct ad1848_mix_elem snd_ad1848_controls[] = { | ||
1222 | AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
1223 | AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), | ||
1224 | AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), | ||
1225 | AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), | ||
1226 | AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), | ||
1227 | AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), | ||
1228 | AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), | ||
1229 | { | ||
1230 | .name = "Capture Source", | ||
1231 | .type = AD1848_MIX_CAPTURE, | ||
1232 | }, | ||
1233 | AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), | ||
1234 | AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) | ||
1235 | }; | ||
1236 | |||
1237 | int snd_ad1848_mixer(ad1848_t *chip) | ||
1238 | { | ||
1239 | snd_card_t *card; | ||
1240 | snd_pcm_t *pcm; | ||
1241 | unsigned int idx; | ||
1242 | int err; | ||
1243 | |||
1244 | snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); | ||
1245 | |||
1246 | pcm = chip->pcm; | ||
1247 | card = chip->card; | ||
1248 | |||
1249 | strcpy(card->mixername, pcm->name); | ||
1250 | |||
1251 | for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) | ||
1252 | if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0) | ||
1253 | return err; | ||
1254 | |||
1255 | return 0; | ||
1256 | } | ||
1257 | |||
1258 | EXPORT_SYMBOL(snd_ad1848_out); | ||
1259 | EXPORT_SYMBOL(snd_ad1848_create); | ||
1260 | EXPORT_SYMBOL(snd_ad1848_pcm); | ||
1261 | EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); | ||
1262 | EXPORT_SYMBOL(snd_ad1848_mixer); | ||
1263 | EXPORT_SYMBOL(snd_ad1848_add_ctl); | ||
1264 | |||
1265 | /* | ||
1266 | * INIT part | ||
1267 | */ | ||
1268 | |||
1269 | static int __init alsa_ad1848_init(void) | ||
1270 | { | ||
1271 | return 0; | ||
1272 | } | ||
1273 | |||
1274 | static void __exit alsa_ad1848_exit(void) | ||
1275 | { | ||
1276 | } | ||
1277 | |||
1278 | module_init(alsa_ad1848_init) | ||
1279 | module_exit(alsa_ad1848_exit) | ||
diff --git a/sound/isa/als100.c b/sound/isa/als100.c new file mode 100644 index 000000000000..ac8f13664983 --- /dev/null +++ b/sound/isa/als100.c | |||
@@ -0,0 +1,336 @@ | |||
1 | |||
2 | /* | ||
3 | card-als100.c - driver for Avance Logic ALS100 based soundcards. | ||
4 | Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> | ||
5 | |||
6 | Thanks to Pierfrancesco 'qM2' Passerini. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2 of the License, or | ||
11 | (at your option) any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; if not, write to the Free Software | ||
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/wait.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/pnp.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/initval.h> | ||
31 | #include <sound/mpu401.h> | ||
32 | #include <sound/opl3.h> | ||
33 | #include <sound/sb.h> | ||
34 | |||
35 | #define PFX "als100: " | ||
36 | |||
37 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
38 | MODULE_DESCRIPTION("Avance Logic ALS1X0"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS100 - PRO16PNP}," | ||
41 | "{Avance Logic,ALS110}," | ||
42 | "{Avance Logic,ALS120}," | ||
43 | "{Avance Logic,ALS200}," | ||
44 | "{3D Melody,MF1000}," | ||
45 | "{Digimate,3D Sound}," | ||
46 | "{Avance Logic,ALS120}," | ||
47 | "{RTL,RTL3000}}"); | ||
48 | |||
49 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
50 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
51 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
52 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
53 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
54 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
55 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ | ||
56 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ | ||
57 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
58 | static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
59 | |||
60 | module_param_array(index, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(index, "Index value for als100 based soundcard."); | ||
62 | module_param_array(id, charp, NULL, 0444); | ||
63 | MODULE_PARM_DESC(id, "ID string for als100 based soundcard."); | ||
64 | module_param_array(enable, bool, NULL, 0444); | ||
65 | MODULE_PARM_DESC(enable, "Enable als100 based soundcard."); | ||
66 | module_param_array(port, long, NULL, 0444); | ||
67 | MODULE_PARM_DESC(port, "Port # for als100 driver."); | ||
68 | module_param_array(mpu_port, long, NULL, 0444); | ||
69 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver."); | ||
70 | module_param_array(fm_port, long, NULL, 0444); | ||
71 | MODULE_PARM_DESC(fm_port, "FM port # for als100 driver."); | ||
72 | module_param_array(irq, int, NULL, 0444); | ||
73 | MODULE_PARM_DESC(irq, "IRQ # for als100 driver."); | ||
74 | module_param_array(mpu_irq, int, NULL, 0444); | ||
75 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver."); | ||
76 | module_param_array(dma8, int, NULL, 0444); | ||
77 | MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver."); | ||
78 | module_param_array(dma16, int, NULL, 0444); | ||
79 | MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver."); | ||
80 | |||
81 | struct snd_card_als100 { | ||
82 | int dev_no; | ||
83 | struct pnp_dev *dev; | ||
84 | struct pnp_dev *devmpu; | ||
85 | struct pnp_dev *devopl; | ||
86 | }; | ||
87 | |||
88 | static struct pnp_card_device_id snd_als100_pnpids[] = { | ||
89 | /* ALS100 - PRO16PNP */ | ||
90 | { .id = "ALS0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } }, | ||
91 | /* ALS110 - MF1000 - Digimate 3D Sound */ | ||
92 | { .id = "ALS0110", .devs = { { "@@@1001" }, { "@X@1001" }, { "@H@1001" } } }, | ||
93 | /* ALS120 */ | ||
94 | { .id = "ALS0120", .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } } }, | ||
95 | /* ALS200 */ | ||
96 | { .id = "ALS0200", .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0001" } } }, | ||
97 | /* ALS200 OEM */ | ||
98 | { .id = "ALS0200", .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0020" } } }, | ||
99 | /* RTL3000 */ | ||
100 | { .id = "RTL3000", .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } } }, | ||
101 | { .id = "", } /* end */ | ||
102 | }; | ||
103 | |||
104 | MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids); | ||
105 | |||
106 | #define DRIVER_NAME "snd-card-als100" | ||
107 | |||
108 | static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, | ||
109 | struct pnp_card_link *card, | ||
110 | const struct pnp_card_device_id *id) | ||
111 | { | ||
112 | struct pnp_dev *pdev; | ||
113 | struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); | ||
114 | int err; | ||
115 | |||
116 | if (!cfg) | ||
117 | return -ENOMEM; | ||
118 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
119 | if (acard->dev == NULL) { | ||
120 | kfree(cfg); | ||
121 | return -ENODEV; | ||
122 | } | ||
123 | acard->devmpu = pnp_request_card_device(card, id->devs[1].id, acard->dev); | ||
124 | acard->devopl = pnp_request_card_device(card, id->devs[2].id, acard->dev); | ||
125 | |||
126 | pdev = acard->dev; | ||
127 | |||
128 | pnp_init_resource_table(cfg); | ||
129 | |||
130 | /* override resources */ | ||
131 | if (port[dev] != SNDRV_AUTO_PORT) | ||
132 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
133 | if (dma8[dev] != SNDRV_AUTO_DMA) | ||
134 | pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); | ||
135 | if (dma16[dev] != SNDRV_AUTO_DMA) | ||
136 | pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1); | ||
137 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
138 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
139 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
140 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
141 | err = pnp_activate_dev(pdev); | ||
142 | if (err < 0) { | ||
143 | snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); | ||
144 | kfree(cfg); | ||
145 | return err; | ||
146 | } | ||
147 | port[dev] = pnp_port_start(pdev, 0); | ||
148 | dma8[dev] = pnp_dma(pdev, 1); | ||
149 | dma16[dev] = pnp_dma(pdev, 0); | ||
150 | irq[dev] = pnp_irq(pdev, 0); | ||
151 | |||
152 | pdev = acard->devmpu; | ||
153 | if (pdev != NULL) { | ||
154 | pnp_init_resource_table(cfg); | ||
155 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
156 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
157 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ) | ||
158 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
159 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
160 | snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n"); | ||
161 | err = pnp_activate_dev(pdev); | ||
162 | if (err < 0) | ||
163 | goto __mpu_error; | ||
164 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
165 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
166 | } else { | ||
167 | __mpu_error: | ||
168 | if (pdev) { | ||
169 | pnp_release_card_device(pdev); | ||
170 | snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n"); | ||
171 | } | ||
172 | acard->devmpu = NULL; | ||
173 | mpu_port[dev] = -1; | ||
174 | } | ||
175 | |||
176 | pdev = acard->devopl; | ||
177 | if (pdev != NULL) { | ||
178 | pnp_init_resource_table(cfg); | ||
179 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
180 | pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4); | ||
181 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
182 | snd_printk(KERN_ERR PFX "OPL3 the requested resources are invalid, using auto config\n"); | ||
183 | err = pnp_activate_dev(pdev); | ||
184 | if (err < 0) | ||
185 | goto __fm_error; | ||
186 | fm_port[dev] = pnp_port_start(pdev, 0); | ||
187 | } else { | ||
188 | __fm_error: | ||
189 | if (pdev) { | ||
190 | pnp_release_card_device(pdev); | ||
191 | snd_printk(KERN_ERR PFX "OPL3 pnp configure failure, skipping\n"); | ||
192 | } | ||
193 | acard->devopl = NULL; | ||
194 | fm_port[dev] = -1; | ||
195 | } | ||
196 | |||
197 | kfree(cfg); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int __init snd_card_als100_probe(int dev, | ||
202 | struct pnp_card_link *pcard, | ||
203 | const struct pnp_card_device_id *pid) | ||
204 | { | ||
205 | int error; | ||
206 | sb_t *chip; | ||
207 | snd_card_t *card; | ||
208 | struct snd_card_als100 *acard; | ||
209 | opl3_t *opl3; | ||
210 | |||
211 | if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
212 | sizeof(struct snd_card_als100))) == NULL) | ||
213 | return -ENOMEM; | ||
214 | acard = (struct snd_card_als100 *)card->private_data; | ||
215 | |||
216 | if ((error = snd_card_als100_pnp(dev, acard, pcard, pid))) { | ||
217 | snd_card_free(card); | ||
218 | return error; | ||
219 | } | ||
220 | snd_card_set_dev(card, &pcard->card->dev); | ||
221 | |||
222 | if ((error = snd_sbdsp_create(card, port[dev], | ||
223 | irq[dev], | ||
224 | snd_sb16dsp_interrupt, | ||
225 | dma8[dev], | ||
226 | dma16[dev], | ||
227 | SB_HW_ALS100, &chip)) < 0) { | ||
228 | snd_card_free(card); | ||
229 | return error; | ||
230 | } | ||
231 | |||
232 | strcpy(card->driver, "ALS100"); | ||
233 | strcpy(card->shortname, "Avance Logic ALS100"); | ||
234 | sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", | ||
235 | card->shortname, chip->name, chip->port, | ||
236 | irq[dev], dma8[dev], dma16[dev]); | ||
237 | |||
238 | if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { | ||
239 | snd_card_free(card); | ||
240 | return error; | ||
241 | } | ||
242 | |||
243 | if ((error = snd_sbmixer_new(chip)) < 0) { | ||
244 | snd_card_free(card); | ||
245 | return error; | ||
246 | } | ||
247 | |||
248 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
249 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100, | ||
250 | mpu_port[dev], 0, | ||
251 | mpu_irq[dev], SA_INTERRUPT, | ||
252 | NULL) < 0) | ||
253 | snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); | ||
254 | } | ||
255 | |||
256 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
257 | if (snd_opl3_create(card, | ||
258 | fm_port[dev], fm_port[dev] + 2, | ||
259 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
260 | snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", | ||
261 | fm_port[dev], fm_port[dev] + 2); | ||
262 | } else { | ||
263 | if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { | ||
264 | snd_card_free(card); | ||
265 | return error; | ||
266 | } | ||
267 | if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
268 | snd_card_free(card); | ||
269 | return error; | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | if ((error = snd_card_register(card)) < 0) { | ||
275 | snd_card_free(card); | ||
276 | return error; | ||
277 | } | ||
278 | pnp_set_card_drvdata(pcard, card); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card, | ||
283 | const struct pnp_card_device_id *id) | ||
284 | { | ||
285 | static int dev; | ||
286 | int res; | ||
287 | |||
288 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
289 | if (!enable[dev]) | ||
290 | continue; | ||
291 | res = snd_card_als100_probe(dev, card, id); | ||
292 | if (res < 0) | ||
293 | return res; | ||
294 | dev++; | ||
295 | return 0; | ||
296 | } | ||
297 | return -ENODEV; | ||
298 | } | ||
299 | |||
300 | static void __devexit snd_als100_pnp_remove(struct pnp_card_link * pcard) | ||
301 | { | ||
302 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
303 | |||
304 | snd_card_disconnect(card); | ||
305 | snd_card_free_in_thread(card); | ||
306 | } | ||
307 | |||
308 | static struct pnp_card_driver als100_pnpc_driver = { | ||
309 | .flags = PNP_DRIVER_RES_DISABLE, | ||
310 | .name = "als100", | ||
311 | .id_table = snd_als100_pnpids, | ||
312 | .probe = snd_als100_pnp_detect, | ||
313 | .remove = __devexit_p(snd_als100_pnp_remove), | ||
314 | }; | ||
315 | |||
316 | static int __init alsa_card_als100_init(void) | ||
317 | { | ||
318 | int cards = 0; | ||
319 | |||
320 | cards += pnp_register_card_driver(&als100_pnpc_driver); | ||
321 | #ifdef MODULE | ||
322 | if (!cards) { | ||
323 | pnp_unregister_card_driver(&als100_pnpc_driver); | ||
324 | snd_printk(KERN_ERR "no ALS100 based soundcards found\n"); | ||
325 | } | ||
326 | #endif | ||
327 | return cards ? 0 : -ENODEV; | ||
328 | } | ||
329 | |||
330 | static void __exit alsa_card_als100_exit(void) | ||
331 | { | ||
332 | pnp_unregister_card_driver(&als100_pnpc_driver); | ||
333 | } | ||
334 | |||
335 | module_init(alsa_card_als100_init) | ||
336 | module_exit(alsa_card_als100_exit) | ||
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c new file mode 100644 index 000000000000..bb41c6ec2f43 --- /dev/null +++ b/sound/isa/azt2320.c | |||
@@ -0,0 +1,366 @@ | |||
1 | /* | ||
2 | card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards. | ||
3 | Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program; if not, write to the Free Software | ||
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | This driver should provide support for most Aztech AZT2320 based cards. | ||
22 | Several AZT2316 chips are also supported/tested, but autoprobe doesn't | ||
23 | work: all module option have to be set. | ||
24 | |||
25 | No docs available for us at Aztech headquarters !!! Unbelievable ... | ||
26 | No other help obtained. | ||
27 | |||
28 | Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS | ||
29 | activation method (full-duplex audio!). | ||
30 | */ | ||
31 | |||
32 | #include <sound/driver.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <linux/delay.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/time.h> | ||
37 | #include <linux/wait.h> | ||
38 | #include <linux/pnp.h> | ||
39 | #include <linux/moduleparam.h> | ||
40 | #include <sound/core.h> | ||
41 | #include <sound/initval.h> | ||
42 | #include <sound/cs4231.h> | ||
43 | #include <sound/mpu401.h> | ||
44 | #include <sound/opl3.h> | ||
45 | |||
46 | #define PFX "azt2320: " | ||
47 | |||
48 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
49 | MODULE_DESCRIPTION("Aztech Systems AZT2320"); | ||
50 | MODULE_LICENSE("GPL"); | ||
51 | MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V}," | ||
52 | "{Aztech Systems,AZT2320}," | ||
53 | "{Aztech Systems,AZT3300}," | ||
54 | "{Aztech Systems,AZT2320}," | ||
55 | "{Aztech Systems,AZT3000}}"); | ||
56 | |||
57 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
58 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
59 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
60 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
61 | static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
62 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
63 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
64 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
65 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
66 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
67 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
68 | |||
69 | module_param_array(index, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard."); | ||
71 | module_param_array(id, charp, NULL, 0444); | ||
72 | MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard."); | ||
73 | module_param_array(enable, bool, NULL, 0444); | ||
74 | MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard."); | ||
75 | module_param_array(port, long, NULL, 0444); | ||
76 | MODULE_PARM_DESC(port, "Port # for azt2320 driver."); | ||
77 | module_param_array(wss_port, long, NULL, 0444); | ||
78 | MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver."); | ||
79 | module_param_array(mpu_port, long, NULL, 0444); | ||
80 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver."); | ||
81 | module_param_array(fm_port, long, NULL, 0444); | ||
82 | MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver."); | ||
83 | module_param_array(irq, int, NULL, 0444); | ||
84 | MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver."); | ||
85 | module_param_array(mpu_irq, int, NULL, 0444); | ||
86 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver."); | ||
87 | module_param_array(dma1, int, NULL, 0444); | ||
88 | MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver."); | ||
89 | module_param_array(dma2, int, NULL, 0444); | ||
90 | MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver."); | ||
91 | |||
92 | struct snd_card_azt2320 { | ||
93 | int dev_no; | ||
94 | struct pnp_dev *dev; | ||
95 | struct pnp_dev *devmpu; | ||
96 | }; | ||
97 | |||
98 | static struct pnp_card_device_id snd_azt2320_pnpids[] = { | ||
99 | /* PRO16V */ | ||
100 | { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } }, | ||
101 | /* Aztech Sound Galaxy 16 */ | ||
102 | { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } }, | ||
103 | /* Packard Bell Sound III 336 AM/SP */ | ||
104 | { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } }, | ||
105 | /* AT3300 */ | ||
106 | { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } }, | ||
107 | /* --- */ | ||
108 | { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } }, | ||
109 | /* --- */ | ||
110 | { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } }, | ||
111 | { .id = "" } /* end */ | ||
112 | }; | ||
113 | |||
114 | MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids); | ||
115 | |||
116 | #define DRIVER_NAME "snd-card-azt2320" | ||
117 | |||
118 | static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard, | ||
119 | struct pnp_card_link *card, | ||
120 | const struct pnp_card_device_id *id) | ||
121 | { | ||
122 | struct pnp_dev *pdev; | ||
123 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
124 | int err; | ||
125 | |||
126 | if (!cfg) | ||
127 | return -ENOMEM; | ||
128 | |||
129 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
130 | if (acard->dev == NULL) { | ||
131 | kfree(cfg); | ||
132 | return -ENODEV; | ||
133 | } | ||
134 | |||
135 | acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
136 | |||
137 | pdev = acard->dev; | ||
138 | pnp_init_resource_table(cfg); | ||
139 | |||
140 | /* override resources */ | ||
141 | if (port[dev] != SNDRV_AUTO_PORT) | ||
142 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
143 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
144 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
145 | if (wss_port[dev] != SNDRV_AUTO_PORT) | ||
146 | pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4); | ||
147 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
148 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
149 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
150 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
151 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
152 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
153 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
154 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
155 | |||
156 | err = pnp_activate_dev(pdev); | ||
157 | if (err < 0) { | ||
158 | snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); | ||
159 | kfree(cfg); | ||
160 | return err; | ||
161 | } | ||
162 | port[dev] = pnp_port_start(pdev, 0); | ||
163 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
164 | wss_port[dev] = pnp_port_start(pdev, 2); | ||
165 | dma1[dev] = pnp_dma(pdev, 0); | ||
166 | dma2[dev] = pnp_dma(pdev, 1); | ||
167 | irq[dev] = pnp_irq(pdev, 0); | ||
168 | |||
169 | pdev = acard->devmpu; | ||
170 | if (pdev != NULL) { | ||
171 | pnp_init_resource_table(cfg); | ||
172 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
173 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
174 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ) | ||
175 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
176 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
177 | snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n"); | ||
178 | err = pnp_activate_dev(pdev); | ||
179 | if (err < 0) | ||
180 | goto __mpu_error; | ||
181 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
182 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
183 | } else { | ||
184 | __mpu_error: | ||
185 | if (pdev) { | ||
186 | pnp_release_card_device(pdev); | ||
187 | snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n"); | ||
188 | } | ||
189 | acard->devmpu = NULL; | ||
190 | mpu_port[dev] = -1; | ||
191 | } | ||
192 | |||
193 | kfree (cfg); | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | /* same of snd_sbdsp_command by Jaroslav Kysela */ | ||
198 | static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val) | ||
199 | { | ||
200 | int i; | ||
201 | unsigned long limit; | ||
202 | |||
203 | limit = jiffies + HZ / 10; | ||
204 | for (i = 50000; i && time_after(limit, jiffies); i--) | ||
205 | if (!(inb(port + 0x0c) & 0x80)) { | ||
206 | outb(val, port + 0x0c); | ||
207 | return 0; | ||
208 | } | ||
209 | return -EBUSY; | ||
210 | } | ||
211 | |||
212 | static int __devinit snd_card_azt2320_enable_wss(unsigned long port) | ||
213 | { | ||
214 | int error; | ||
215 | |||
216 | if ((error = snd_card_azt2320_command(port, 0x09))) | ||
217 | return error; | ||
218 | if ((error = snd_card_azt2320_command(port, 0x00))) | ||
219 | return error; | ||
220 | |||
221 | mdelay(5); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int __devinit snd_card_azt2320_probe(int dev, | ||
226 | struct pnp_card_link *pcard, | ||
227 | const struct pnp_card_device_id *pid) | ||
228 | { | ||
229 | int error; | ||
230 | snd_card_t *card; | ||
231 | struct snd_card_azt2320 *acard; | ||
232 | cs4231_t *chip; | ||
233 | opl3_t *opl3; | ||
234 | |||
235 | if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
236 | sizeof(struct snd_card_azt2320))) == NULL) | ||
237 | return -ENOMEM; | ||
238 | acard = (struct snd_card_azt2320 *)card->private_data; | ||
239 | |||
240 | if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) { | ||
241 | snd_card_free(card); | ||
242 | return error; | ||
243 | } | ||
244 | snd_card_set_dev(card, &pcard->card->dev); | ||
245 | |||
246 | if ((error = snd_card_azt2320_enable_wss(port[dev]))) { | ||
247 | snd_card_free(card); | ||
248 | return error; | ||
249 | } | ||
250 | |||
251 | if ((error = snd_cs4231_create(card, wss_port[dev], -1, | ||
252 | irq[dev], | ||
253 | dma1[dev], | ||
254 | dma2[dev], | ||
255 | CS4231_HW_DETECT, 0, &chip)) < 0) { | ||
256 | snd_card_free(card); | ||
257 | return error; | ||
258 | } | ||
259 | |||
260 | strcpy(card->driver, "AZT2320"); | ||
261 | strcpy(card->shortname, "Aztech AZT2320"); | ||
262 | sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i", | ||
263 | card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); | ||
264 | |||
265 | if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) { | ||
266 | snd_card_free(card); | ||
267 | return error; | ||
268 | } | ||
269 | if ((error = snd_cs4231_mixer(chip)) < 0) { | ||
270 | snd_card_free(card); | ||
271 | return error; | ||
272 | } | ||
273 | if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) { | ||
274 | snd_card_free(card); | ||
275 | return error; | ||
276 | } | ||
277 | |||
278 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
279 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320, | ||
280 | mpu_port[dev], 0, | ||
281 | mpu_irq[dev], SA_INTERRUPT, | ||
282 | NULL) < 0) | ||
283 | snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); | ||
284 | } | ||
285 | |||
286 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
287 | if (snd_opl3_create(card, | ||
288 | fm_port[dev], fm_port[dev] + 2, | ||
289 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
290 | snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", | ||
291 | fm_port[dev], fm_port[dev] + 2); | ||
292 | } else { | ||
293 | if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { | ||
294 | snd_card_free(card); | ||
295 | return error; | ||
296 | } | ||
297 | if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
298 | snd_card_free(card); | ||
299 | return error; | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | |||
304 | if ((error = snd_card_register(card)) < 0) { | ||
305 | snd_card_free(card); | ||
306 | return error; | ||
307 | } | ||
308 | pnp_set_card_drvdata(pcard, card); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card, | ||
313 | const struct pnp_card_device_id *id) | ||
314 | { | ||
315 | static int dev; | ||
316 | int res; | ||
317 | |||
318 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
319 | if (!enable[dev]) | ||
320 | continue; | ||
321 | res = snd_card_azt2320_probe(dev, card, id); | ||
322 | if (res < 0) | ||
323 | return res; | ||
324 | dev++; | ||
325 | return 0; | ||
326 | } | ||
327 | return -ENODEV; | ||
328 | } | ||
329 | |||
330 | static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard) | ||
331 | { | ||
332 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
333 | |||
334 | snd_card_disconnect(card); | ||
335 | snd_card_free_in_thread(card); | ||
336 | } | ||
337 | |||
338 | static struct pnp_card_driver azt2320_pnpc_driver = { | ||
339 | .flags = PNP_DRIVER_RES_DISABLE, | ||
340 | .name = "azt2320", | ||
341 | .id_table = snd_azt2320_pnpids, | ||
342 | .probe = snd_azt2320_pnp_detect, | ||
343 | .remove = __devexit_p(snd_azt2320_pnp_remove), | ||
344 | }; | ||
345 | |||
346 | static int __init alsa_card_azt2320_init(void) | ||
347 | { | ||
348 | int cards = 0; | ||
349 | |||
350 | cards += pnp_register_card_driver(&azt2320_pnpc_driver); | ||
351 | #ifdef MODULE | ||
352 | if (!cards) { | ||
353 | pnp_unregister_card_driver(&azt2320_pnpc_driver); | ||
354 | snd_printk(KERN_ERR "no AZT2320 based soundcards found\n"); | ||
355 | } | ||
356 | #endif | ||
357 | return cards ? 0 : -ENODEV; | ||
358 | } | ||
359 | |||
360 | static void __exit alsa_card_azt2320_exit(void) | ||
361 | { | ||
362 | pnp_unregister_card_driver(&azt2320_pnpc_driver); | ||
363 | } | ||
364 | |||
365 | module_init(alsa_card_azt2320_init) | ||
366 | module_exit(alsa_card_azt2320_exit) | ||
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c new file mode 100644 index 000000000000..46776cc0c157 --- /dev/null +++ b/sound/isa/cmi8330.c | |||
@@ -0,0 +1,633 @@ | |||
1 | /* | ||
2 | * Driver for C-Media's CMI8330 soundcards. | ||
3 | * Copyright (c) by George Talusan <gstalusan@uwaterloo.ca> | ||
4 | * http://www.undergrad.math.uwaterloo.ca/~gstalusa | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * NOTES | ||
24 | * | ||
25 | * The extended registers contain mixer settings which are largely | ||
26 | * untapped for the time being. | ||
27 | * | ||
28 | * MPU401 and SPDIF are not supported yet. I don't have the hardware | ||
29 | * to aid in coding and testing, so I won't bother. | ||
30 | * | ||
31 | * To quickly load the module, | ||
32 | * | ||
33 | * modprobe -a snd-cmi8330 sbport=0x220 sbirq=5 sbdma8=1 | ||
34 | * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0 | ||
35 | * | ||
36 | * This card has two mixers and two PCM devices. I've cheesed it such | ||
37 | * that recording and playback can be done through the same device. | ||
38 | * The driver "magically" routes the capturing to the AD1848 codec, | ||
39 | * and playback to the SB16 codec. This allows for full-duplex mode | ||
40 | * to some extent. | ||
41 | * The utilities in alsa-utils are aware of both devices, so passing | ||
42 | * the appropriate parameters to amixer and alsactl will give you | ||
43 | * full control over both mixers. | ||
44 | */ | ||
45 | |||
46 | #include <sound/driver.h> | ||
47 | #include <linux/init.h> | ||
48 | #include <linux/slab.h> | ||
49 | #include <linux/pnp.h> | ||
50 | #include <linux/moduleparam.h> | ||
51 | #include <sound/core.h> | ||
52 | #include <sound/ad1848.h> | ||
53 | #include <sound/sb.h> | ||
54 | #include <sound/initval.h> | ||
55 | |||
56 | /* | ||
57 | */ | ||
58 | /* #define ENABLE_SB_MIXER */ | ||
59 | #define PLAYBACK_ON_SB | ||
60 | |||
61 | /* | ||
62 | */ | ||
63 | MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>"); | ||
64 | MODULE_DESCRIPTION("C-Media CMI8330"); | ||
65 | MODULE_LICENSE("GPL"); | ||
66 | MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); | ||
67 | |||
68 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
69 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
70 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; | ||
71 | #ifdef CONFIG_PNP | ||
72 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
73 | #endif | ||
74 | static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
75 | static int sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | ||
76 | static int sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
77 | static int sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
78 | static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
79 | static int wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | ||
80 | static int wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
81 | |||
82 | module_param_array(index, int, NULL, 0444); | ||
83 | MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard."); | ||
84 | module_param_array(id, charp, NULL, 0444); | ||
85 | MODULE_PARM_DESC(id, "ID string for CMI8330 soundcard."); | ||
86 | module_param_array(enable, bool, NULL, 0444); | ||
87 | MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard."); | ||
88 | #ifdef CONFIG_PNP | ||
89 | module_param_array(isapnp, bool, NULL, 0444); | ||
90 | MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); | ||
91 | #endif | ||
92 | |||
93 | module_param_array(sbport, long, NULL, 0444); | ||
94 | MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver."); | ||
95 | module_param_array(sbirq, int, NULL, 0444); | ||
96 | MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver."); | ||
97 | module_param_array(sbdma8, int, NULL, 0444); | ||
98 | MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver."); | ||
99 | module_param_array(sbdma16, int, NULL, 0444); | ||
100 | MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver."); | ||
101 | |||
102 | module_param_array(wssport, long, NULL, 0444); | ||
103 | MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver."); | ||
104 | module_param_array(wssirq, int, NULL, 0444); | ||
105 | MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver."); | ||
106 | module_param_array(wssdma, int, NULL, 0444); | ||
107 | MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver."); | ||
108 | |||
109 | #define CMI8330_RMUX3D 16 | ||
110 | #define CMI8330_MUTEMUX 17 | ||
111 | #define CMI8330_OUTPUTVOL 18 | ||
112 | #define CMI8330_MASTVOL 19 | ||
113 | #define CMI8330_LINVOL 20 | ||
114 | #define CMI8330_CDINVOL 21 | ||
115 | #define CMI8330_WAVVOL 22 | ||
116 | #define CMI8330_RECMUX 23 | ||
117 | #define CMI8330_WAVGAIN 24 | ||
118 | #define CMI8330_LINGAIN 25 | ||
119 | #define CMI8330_CDINGAIN 26 | ||
120 | |||
121 | static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = | ||
122 | { | ||
123 | 0x40, /* 16 - recording mux (SB-mixer-enabled) */ | ||
124 | #ifdef ENABLE_SB_MIXER | ||
125 | 0x40, /* 17 - mute mux (Mode2) */ | ||
126 | #else | ||
127 | 0x0, /* 17 - mute mux */ | ||
128 | #endif | ||
129 | 0x0, /* 18 - vol */ | ||
130 | 0x0, /* 19 - master volume */ | ||
131 | 0x0, /* 20 - line-in volume */ | ||
132 | 0x0, /* 21 - cd-in volume */ | ||
133 | 0x0, /* 22 - wave volume */ | ||
134 | 0x0, /* 23 - mute/rec mux */ | ||
135 | 0x0, /* 24 - wave rec gain */ | ||
136 | 0x0, /* 25 - line-in rec gain */ | ||
137 | 0x0 /* 26 - cd-in rec gain */ | ||
138 | }; | ||
139 | |||
140 | typedef int (*snd_pcm_open_callback_t)(snd_pcm_substream_t *); | ||
141 | |||
142 | struct snd_cmi8330 { | ||
143 | #ifdef CONFIG_PNP | ||
144 | struct pnp_dev *cap; | ||
145 | struct pnp_dev *play; | ||
146 | #endif | ||
147 | snd_card_t *card; | ||
148 | ad1848_t *wss; | ||
149 | sb_t *sb; | ||
150 | |||
151 | snd_pcm_t *pcm; | ||
152 | struct snd_cmi8330_stream { | ||
153 | snd_pcm_ops_t ops; | ||
154 | snd_pcm_open_callback_t open; | ||
155 | void *private_data; /* sb or wss */ | ||
156 | } streams[2]; | ||
157 | }; | ||
158 | |||
159 | static snd_card_t *snd_cmi8330_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
160 | |||
161 | #ifdef CONFIG_PNP | ||
162 | |||
163 | static struct pnp_card_device_id snd_cmi8330_pnpids[] = { | ||
164 | { .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" } } }, | ||
165 | { .id = "" } | ||
166 | }; | ||
167 | |||
168 | MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids); | ||
169 | |||
170 | #endif | ||
171 | |||
172 | |||
173 | static struct ad1848_mix_elem snd_cmi8330_controls[] __initdata = { | ||
174 | AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), | ||
175 | AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), | ||
176 | AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
177 | AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), | ||
178 | AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), | ||
179 | AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), | ||
180 | AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), | ||
181 | AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), | ||
182 | AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), | ||
183 | AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), | ||
184 | AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), | ||
185 | AD1848_DOUBLE("CD Capture Volume", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), | ||
186 | AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0), | ||
187 | AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0), | ||
188 | AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0), | ||
189 | AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0), | ||
190 | AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), | ||
191 | AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), | ||
192 | AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), | ||
193 | AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), | ||
194 | AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1), | ||
195 | AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0), | ||
196 | AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1), | ||
197 | AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1), | ||
198 | AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1), | ||
199 | }; | ||
200 | |||
201 | #ifdef ENABLE_SB_MIXER | ||
202 | static struct sbmix_elem cmi8330_sb_mixers[] __initdata = { | ||
203 | SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), | ||
204 | SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), | ||
205 | SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), | ||
206 | SB_DOUBLE("SB PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), | ||
207 | SB_DOUBLE("SB Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), | ||
208 | SB_DOUBLE("SB CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), | ||
209 | SB_DOUBLE("SB CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), | ||
210 | SB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), | ||
211 | SB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), | ||
212 | SB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1), | ||
213 | SB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), | ||
214 | SB_SINGLE("SB PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3), | ||
215 | SB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), | ||
216 | SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), | ||
217 | SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1), | ||
218 | }; | ||
219 | |||
220 | static unsigned char cmi8330_sb_init_values[][2] __initdata = { | ||
221 | { SB_DSP4_MASTER_DEV + 0, 0 }, | ||
222 | { SB_DSP4_MASTER_DEV + 1, 0 }, | ||
223 | { SB_DSP4_PCM_DEV + 0, 0 }, | ||
224 | { SB_DSP4_PCM_DEV + 1, 0 }, | ||
225 | { SB_DSP4_SYNTH_DEV + 0, 0 }, | ||
226 | { SB_DSP4_SYNTH_DEV + 1, 0 }, | ||
227 | { SB_DSP4_INPUT_LEFT, 0 }, | ||
228 | { SB_DSP4_INPUT_RIGHT, 0 }, | ||
229 | { SB_DSP4_OUTPUT_SW, 0 }, | ||
230 | { SB_DSP4_SPEAKER_DEV, 0 }, | ||
231 | }; | ||
232 | |||
233 | |||
234 | static int __devinit cmi8330_add_sb_mixers(sb_t *chip) | ||
235 | { | ||
236 | int idx, err; | ||
237 | unsigned long flags; | ||
238 | |||
239 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
240 | snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */ | ||
241 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
242 | |||
243 | /* mute and zero volume channels */ | ||
244 | for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_init_values); idx++) { | ||
245 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
246 | snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0], | ||
247 | cmi8330_sb_init_values[idx][1]); | ||
248 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
249 | } | ||
250 | |||
251 | for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_mixers); idx++) { | ||
252 | if ((err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx])) < 0) | ||
253 | return err; | ||
254 | } | ||
255 | return 0; | ||
256 | } | ||
257 | #endif | ||
258 | |||
259 | static int __devinit snd_cmi8330_mixer(snd_card_t *card, struct snd_cmi8330 *acard) | ||
260 | { | ||
261 | unsigned int idx; | ||
262 | int err; | ||
263 | |||
264 | strcpy(card->mixername, "CMI8330/C3D"); | ||
265 | |||
266 | for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { | ||
267 | if ((err = snd_ad1848_add_ctl_elem(acard->wss, &snd_cmi8330_controls[idx])) < 0) | ||
268 | return err; | ||
269 | } | ||
270 | |||
271 | #ifdef ENABLE_SB_MIXER | ||
272 | if ((err = cmi8330_add_sb_mixers(acard->sb)) < 0) | ||
273 | return err; | ||
274 | #endif | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | #ifdef CONFIG_PNP | ||
279 | static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, | ||
280 | struct pnp_card_link *card, | ||
281 | const struct pnp_card_device_id *id) | ||
282 | { | ||
283 | struct pnp_dev *pdev; | ||
284 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
285 | int err; | ||
286 | |||
287 | acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
288 | if (acard->cap == NULL) { | ||
289 | kfree(cfg); | ||
290 | return -EBUSY; | ||
291 | } | ||
292 | acard->play = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
293 | if (acard->play == NULL) { | ||
294 | kfree(cfg); | ||
295 | return -EBUSY; | ||
296 | } | ||
297 | |||
298 | pdev = acard->cap; | ||
299 | pnp_init_resource_table(cfg); | ||
300 | /* allocate AD1848 resources */ | ||
301 | if (wssport[dev] != SNDRV_AUTO_PORT) | ||
302 | pnp_resource_change(&cfg->port_resource[0], wssport[dev], 8); | ||
303 | if (wssdma[dev] != SNDRV_AUTO_DMA) | ||
304 | pnp_resource_change(&cfg->dma_resource[0], wssdma[dev], 1); | ||
305 | if (wssirq[dev] != SNDRV_AUTO_IRQ) | ||
306 | pnp_resource_change(&cfg->irq_resource[0], wssirq[dev], 1); | ||
307 | |||
308 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
309 | if (err < 0) | ||
310 | snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP manual resources are invalid, using auto config\n"); | ||
311 | err = pnp_activate_dev(pdev); | ||
312 | if (err < 0) { | ||
313 | snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP configure failure\n"); | ||
314 | kfree(cfg); | ||
315 | return -EBUSY; | ||
316 | } | ||
317 | wssport[dev] = pnp_port_start(pdev, 0); | ||
318 | wssdma[dev] = pnp_dma(pdev, 0); | ||
319 | wssirq[dev] = pnp_irq(pdev, 0); | ||
320 | |||
321 | /* allocate SB16 resources */ | ||
322 | pdev = acard->play; | ||
323 | pnp_init_resource_table(cfg); | ||
324 | if (sbport[dev] != SNDRV_AUTO_PORT) | ||
325 | pnp_resource_change(&cfg->port_resource[0], sbport[dev], 16); | ||
326 | if (sbdma8[dev] != SNDRV_AUTO_DMA) | ||
327 | pnp_resource_change(&cfg->dma_resource[0], sbdma8[dev], 1); | ||
328 | if (sbdma16[dev] != SNDRV_AUTO_DMA) | ||
329 | pnp_resource_change(&cfg->dma_resource[1], sbdma16[dev], 1); | ||
330 | if (sbirq[dev] != SNDRV_AUTO_IRQ) | ||
331 | pnp_resource_change(&cfg->irq_resource[0], sbirq[dev], 1); | ||
332 | |||
333 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
334 | if (err < 0) | ||
335 | snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP manual resources are invalid, using auto config\n"); | ||
336 | err = pnp_activate_dev(pdev); | ||
337 | if (err < 0) { | ||
338 | snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n"); | ||
339 | kfree(cfg); | ||
340 | return -EBUSY; | ||
341 | } | ||
342 | sbport[dev] = pnp_port_start(pdev, 0); | ||
343 | sbdma8[dev] = pnp_dma(pdev, 0); | ||
344 | sbdma16[dev] = pnp_dma(pdev, 1); | ||
345 | sbirq[dev] = pnp_irq(pdev, 0); | ||
346 | |||
347 | kfree(cfg); | ||
348 | return 0; | ||
349 | } | ||
350 | #endif | ||
351 | |||
352 | /* | ||
353 | * PCM interface | ||
354 | * | ||
355 | * since we call the different chip interfaces for playback and capture | ||
356 | * directions, we need a trick. | ||
357 | * | ||
358 | * - copy the ops for each direction into a local record. | ||
359 | * - replace the open callback with the new one, which replaces the | ||
360 | * substream->private_data with the corresponding chip instance | ||
361 | * and calls again the original open callback of the chip. | ||
362 | * | ||
363 | */ | ||
364 | |||
365 | #ifdef PLAYBACK_ON_SB | ||
366 | #define CMI_SB_STREAM SNDRV_PCM_STREAM_PLAYBACK | ||
367 | #define CMI_AD_STREAM SNDRV_PCM_STREAM_CAPTURE | ||
368 | #else | ||
369 | #define CMI_SB_STREAM SNDRV_PCM_STREAM_CAPTURE | ||
370 | #define CMI_AD_STREAM SNDRV_PCM_STREAM_PLAYBACK | ||
371 | #endif | ||
372 | |||
373 | static int snd_cmi8330_playback_open(snd_pcm_substream_t * substream) | ||
374 | { | ||
375 | struct snd_cmi8330 *chip = snd_pcm_substream_chip(substream); | ||
376 | |||
377 | /* replace the private_data and call the original open callback */ | ||
378 | substream->private_data = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].private_data; | ||
379 | return chip->streams[SNDRV_PCM_STREAM_PLAYBACK].open(substream); | ||
380 | } | ||
381 | |||
382 | static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) | ||
383 | { | ||
384 | struct snd_cmi8330 *chip = snd_pcm_substream_chip(substream); | ||
385 | |||
386 | /* replace the private_data and call the original open callback */ | ||
387 | substream->private_data = chip->streams[SNDRV_PCM_STREAM_CAPTURE].private_data; | ||
388 | return chip->streams[SNDRV_PCM_STREAM_CAPTURE].open(substream); | ||
389 | } | ||
390 | |||
391 | static void snd_cmi8330_pcm_free(snd_pcm_t *pcm) | ||
392 | { | ||
393 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
394 | } | ||
395 | |||
396 | static int __devinit snd_cmi8330_pcm(snd_card_t *card, struct snd_cmi8330 *chip) | ||
397 | { | ||
398 | snd_pcm_t *pcm; | ||
399 | const snd_pcm_ops_t *ops; | ||
400 | int err; | ||
401 | static snd_pcm_open_callback_t cmi_open_callbacks[2] = { | ||
402 | snd_cmi8330_playback_open, | ||
403 | snd_cmi8330_capture_open | ||
404 | }; | ||
405 | |||
406 | if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0) | ||
407 | return err; | ||
408 | strcpy(pcm->name, "CMI8330"); | ||
409 | pcm->private_data = chip; | ||
410 | pcm->private_free = snd_cmi8330_pcm_free; | ||
411 | |||
412 | /* SB16 */ | ||
413 | ops = snd_sb16dsp_get_pcm_ops(CMI_SB_STREAM); | ||
414 | chip->streams[CMI_SB_STREAM].ops = *ops; | ||
415 | chip->streams[CMI_SB_STREAM].open = ops->open; | ||
416 | chip->streams[CMI_SB_STREAM].ops.open = cmi_open_callbacks[CMI_SB_STREAM]; | ||
417 | chip->streams[CMI_SB_STREAM].private_data = chip->sb; | ||
418 | |||
419 | /* AD1848 */ | ||
420 | ops = snd_ad1848_get_pcm_ops(CMI_AD_STREAM); | ||
421 | chip->streams[CMI_AD_STREAM].ops = *ops; | ||
422 | chip->streams[CMI_AD_STREAM].open = ops->open; | ||
423 | chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM]; | ||
424 | chip->streams[CMI_AD_STREAM].private_data = chip->wss; | ||
425 | |||
426 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops); | ||
427 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); | ||
428 | |||
429 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
430 | snd_dma_isa_data(), | ||
431 | 64*1024, 128*1024); | ||
432 | chip->pcm = pcm; | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | |||
438 | /* | ||
439 | */ | ||
440 | |||
441 | static int __devinit snd_cmi8330_probe(int dev, | ||
442 | struct pnp_card_link *pcard, | ||
443 | const struct pnp_card_device_id *pid) | ||
444 | { | ||
445 | snd_card_t *card; | ||
446 | struct snd_cmi8330 *acard; | ||
447 | unsigned long flags; | ||
448 | int i, err; | ||
449 | |||
450 | #ifdef CONFIG_PNP | ||
451 | if (!isapnp[dev]) { | ||
452 | #endif | ||
453 | if (wssport[dev] == SNDRV_AUTO_PORT) { | ||
454 | snd_printk("specify wssport\n"); | ||
455 | return -EINVAL; | ||
456 | } | ||
457 | if (sbport[dev] == SNDRV_AUTO_PORT) { | ||
458 | snd_printk("specify sbport\n"); | ||
459 | return -EINVAL; | ||
460 | } | ||
461 | #ifdef CONFIG_PNP | ||
462 | } | ||
463 | #endif | ||
464 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
465 | sizeof(struct snd_cmi8330)); | ||
466 | if (card == NULL) { | ||
467 | snd_printk("could not get a new card\n"); | ||
468 | return -ENOMEM; | ||
469 | } | ||
470 | acard = (struct snd_cmi8330 *)card->private_data; | ||
471 | acard->card = card; | ||
472 | |||
473 | #ifdef CONFIG_PNP | ||
474 | if (isapnp[dev]) { | ||
475 | if ((err = snd_cmi8330_pnp(dev, acard, pcard, pid)) < 0) { | ||
476 | snd_printk("PnP detection failed\n"); | ||
477 | snd_card_free(card); | ||
478 | return err; | ||
479 | } | ||
480 | snd_card_set_dev(card, &pcard->card->dev); | ||
481 | } | ||
482 | #endif | ||
483 | |||
484 | if ((err = snd_ad1848_create(card, | ||
485 | wssport[dev] + 4, | ||
486 | wssirq[dev], | ||
487 | wssdma[dev], | ||
488 | AD1848_HW_DETECT, | ||
489 | &acard->wss)) < 0) { | ||
490 | snd_printk("(AD1848) device busy??\n"); | ||
491 | snd_card_free(card); | ||
492 | return err; | ||
493 | } | ||
494 | if (acard->wss->hardware != AD1848_HW_CMI8330) { | ||
495 | snd_printk("(AD1848) not found during probe\n"); | ||
496 | snd_card_free(card); | ||
497 | return -ENODEV; | ||
498 | } | ||
499 | |||
500 | if ((err = snd_sbdsp_create(card, sbport[dev], | ||
501 | sbirq[dev], | ||
502 | snd_sb16dsp_interrupt, | ||
503 | sbdma8[dev], | ||
504 | sbdma16[dev], | ||
505 | SB_HW_AUTO, &acard->sb)) < 0) { | ||
506 | snd_printk("(SB16) device busy??\n"); | ||
507 | snd_card_free(card); | ||
508 | return err; | ||
509 | } | ||
510 | if (acard->sb->hardware != SB_HW_16) { | ||
511 | snd_printk("(SB16) not found during probe\n"); | ||
512 | snd_card_free(card); | ||
513 | return -ENODEV; | ||
514 | } | ||
515 | |||
516 | spin_lock_irqsave(&acard->wss->reg_lock, flags); | ||
517 | snd_ad1848_out(acard->wss, AD1848_MISC_INFO, 0x40); /* switch on MODE2 */ | ||
518 | for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) | ||
519 | snd_ad1848_out(acard->wss, i, snd_cmi8330_image[i - CMI8330_RMUX3D]); | ||
520 | spin_unlock_irqrestore(&acard->wss->reg_lock, flags); | ||
521 | |||
522 | if ((err = snd_cmi8330_mixer(card, acard)) < 0) { | ||
523 | snd_printk("failed to create mixers\n"); | ||
524 | snd_card_free(card); | ||
525 | return err; | ||
526 | } | ||
527 | |||
528 | if ((err = snd_cmi8330_pcm(card, acard)) < 0) { | ||
529 | snd_printk("failed to create pcms\n"); | ||
530 | snd_card_free(card); | ||
531 | return err; | ||
532 | } | ||
533 | |||
534 | strcpy(card->driver, "CMI8330/C3D"); | ||
535 | strcpy(card->shortname, "C-Media CMI8330/C3D"); | ||
536 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||
537 | card->shortname, | ||
538 | acard->wss->port, | ||
539 | wssirq[dev], | ||
540 | wssdma[dev]); | ||
541 | |||
542 | if ((err = snd_card_register(card)) < 0) { | ||
543 | snd_card_free(card); | ||
544 | return err; | ||
545 | } | ||
546 | |||
547 | if (pcard) | ||
548 | pnp_set_card_drvdata(pcard, card); | ||
549 | else | ||
550 | snd_cmi8330_legacy[dev] = card; | ||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | #ifdef CONFIG_PNP | ||
555 | static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *card, | ||
556 | const struct pnp_card_device_id *id) | ||
557 | { | ||
558 | static int dev; | ||
559 | int res; | ||
560 | |||
561 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
562 | if (!enable[dev] || !isapnp[dev]) | ||
563 | continue; | ||
564 | res = snd_cmi8330_probe(dev, card, id); | ||
565 | if (res < 0) | ||
566 | return res; | ||
567 | dev++; | ||
568 | return 0; | ||
569 | } | ||
570 | return -ENODEV; | ||
571 | } | ||
572 | |||
573 | static void __devexit snd_cmi8330_pnp_remove(struct pnp_card_link * pcard) | ||
574 | { | ||
575 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
576 | |||
577 | snd_card_disconnect(card); | ||
578 | snd_card_free_in_thread(card); | ||
579 | } | ||
580 | |||
581 | static struct pnp_card_driver cmi8330_pnpc_driver = { | ||
582 | .flags = PNP_DRIVER_RES_DISABLE, | ||
583 | .name = "cmi8330", | ||
584 | .id_table = snd_cmi8330_pnpids, | ||
585 | .probe = snd_cmi8330_pnp_detect, | ||
586 | .remove = __devexit_p(snd_cmi8330_pnp_remove), | ||
587 | }; | ||
588 | #endif /* CONFIG_PNP */ | ||
589 | |||
590 | static int __init alsa_card_cmi8330_init(void) | ||
591 | { | ||
592 | int dev, cards = 0; | ||
593 | |||
594 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
595 | if (!enable[dev]) | ||
596 | continue; | ||
597 | #ifdef CONFIG_PNP | ||
598 | if (isapnp[dev]) | ||
599 | continue; | ||
600 | #endif | ||
601 | if (snd_cmi8330_probe(dev, NULL, NULL) >= 0) | ||
602 | cards++; | ||
603 | } | ||
604 | #ifdef CONFIG_PNP | ||
605 | cards += pnp_register_card_driver(&cmi8330_pnpc_driver); | ||
606 | #endif | ||
607 | |||
608 | if (!cards) { | ||
609 | #ifdef CONFIG_PNP | ||
610 | pnp_unregister_card_driver(&cmi8330_pnpc_driver); | ||
611 | #endif | ||
612 | #ifdef MODULE | ||
613 | snd_printk(KERN_ERR "CMI8330 not found or device busy\n"); | ||
614 | #endif | ||
615 | return -ENODEV; | ||
616 | } | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static void __exit alsa_card_cmi8330_exit(void) | ||
621 | { | ||
622 | int i; | ||
623 | |||
624 | #ifdef CONFIG_PNP | ||
625 | /* PnP cards first */ | ||
626 | pnp_unregister_card_driver(&cmi8330_pnpc_driver); | ||
627 | #endif | ||
628 | for (i = 0; i < SNDRV_CARDS; i++) | ||
629 | snd_card_free(snd_cmi8330_legacy[i]); | ||
630 | } | ||
631 | |||
632 | module_init(alsa_card_cmi8330_init) | ||
633 | module_exit(alsa_card_cmi8330_exit) | ||
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile new file mode 100644 index 000000000000..d2afaea30cbc --- /dev/null +++ b/sound/isa/cs423x/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-cs4231-lib-objs := cs4231_lib.o | ||
7 | snd-cs4236-lib-objs := cs4236_lib.o | ||
8 | snd-cs4231-objs := cs4231.o | ||
9 | snd-cs4232-objs := cs4232.o | ||
10 | snd-cs4236-objs := cs4236.o | ||
11 | |||
12 | # Toplevel Module Dependency | ||
13 | obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o | ||
14 | obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o | ||
15 | obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o | ||
16 | obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o | ||
17 | obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o | ||
18 | obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o | ||
19 | obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o | ||
20 | obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o | ||
21 | obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o | ||
22 | obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o | ||
23 | obj-$(CONFIG_SND_SSCAPE) += snd-cs4231-lib.o | ||
24 | |||
25 | obj-m := $(sort $(obj-m)) | ||
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c new file mode 100644 index 000000000000..7640837659ea --- /dev/null +++ b/sound/isa/cs423x/cs4231.c | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * Generic driver for CS4231 chips | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * Originally the CS4232/CS4232A driver, modified for use on CS4231 by | ||
5 | * Tugrul Galatali <galatalt@stuy.edu> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/cs4231.h> | ||
30 | #include <sound/mpu401.h> | ||
31 | #include <sound/initval.h> | ||
32 | |||
33 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
34 | MODULE_DESCRIPTION("Generic CS4231"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4231}}"); | ||
37 | |||
38 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
39 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
40 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
41 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
42 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
43 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ | ||
44 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ | ||
45 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
46 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
47 | |||
48 | module_param_array(index, int, NULL, 0444); | ||
49 | MODULE_PARM_DESC(index, "Index value for CS4231 soundcard."); | ||
50 | module_param_array(id, charp, NULL, 0444); | ||
51 | MODULE_PARM_DESC(id, "ID string for CS4231 soundcard."); | ||
52 | module_param_array(enable, bool, NULL, 0444); | ||
53 | MODULE_PARM_DESC(enable, "Enable CS4231 soundcard."); | ||
54 | module_param_array(port, long, NULL, 0444); | ||
55 | MODULE_PARM_DESC(port, "Port # for CS4231 driver."); | ||
56 | module_param_array(mpu_port, long, NULL, 0444); | ||
57 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for CS4231 driver."); | ||
58 | module_param_array(irq, int, NULL, 0444); | ||
59 | MODULE_PARM_DESC(irq, "IRQ # for CS4231 driver."); | ||
60 | module_param_array(mpu_irq, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for CS4231 driver."); | ||
62 | module_param_array(dma1, int, NULL, 0444); | ||
63 | MODULE_PARM_DESC(dma1, "DMA1 # for CS4231 driver."); | ||
64 | module_param_array(dma2, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(dma2, "DMA2 # for CS4231 driver."); | ||
66 | |||
67 | static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
68 | |||
69 | |||
70 | static int __init snd_card_cs4231_probe(int dev) | ||
71 | { | ||
72 | snd_card_t *card; | ||
73 | struct snd_card_cs4231 *acard; | ||
74 | snd_pcm_t *pcm = NULL; | ||
75 | cs4231_t *chip; | ||
76 | int err; | ||
77 | |||
78 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
79 | snd_printk("specify port\n"); | ||
80 | return -EINVAL; | ||
81 | } | ||
82 | if (irq[dev] == SNDRV_AUTO_IRQ) { | ||
83 | snd_printk("specify irq\n"); | ||
84 | return -EINVAL; | ||
85 | } | ||
86 | if (dma1[dev] == SNDRV_AUTO_DMA) { | ||
87 | snd_printk("specify dma1\n"); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
91 | if (card == NULL) | ||
92 | return -ENOMEM; | ||
93 | acard = (struct snd_card_cs4231 *)card->private_data; | ||
94 | if ((err = snd_cs4231_create(card, port[dev], -1, | ||
95 | irq[dev], | ||
96 | dma1[dev], | ||
97 | dma2[dev], | ||
98 | CS4231_HW_DETECT, | ||
99 | 0, &chip)) < 0) { | ||
100 | snd_card_free(card); | ||
101 | return err; | ||
102 | } | ||
103 | |||
104 | if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { | ||
105 | snd_card_free(card); | ||
106 | return err; | ||
107 | } | ||
108 | |||
109 | strcpy(card->driver, "CS4231"); | ||
110 | strcpy(card->shortname, pcm->name); | ||
111 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||
112 | pcm->name, chip->port, irq[dev], dma1[dev]); | ||
113 | if (dma2[dev] >= 0) | ||
114 | sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); | ||
115 | |||
116 | if ((err = snd_cs4231_mixer(chip)) < 0) { | ||
117 | snd_card_free(card); | ||
118 | return err; | ||
119 | } | ||
120 | if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { | ||
121 | snd_card_free(card); | ||
122 | return err; | ||
123 | } | ||
124 | |||
125 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
126 | if (mpu_irq[dev] == SNDRV_AUTO_IRQ) | ||
127 | mpu_irq[dev] = -1; | ||
128 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, | ||
129 | mpu_port[dev], 0, | ||
130 | mpu_irq[dev], | ||
131 | mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, | ||
132 | NULL) < 0) | ||
133 | printk(KERN_ERR "cs4231: MPU401 not detected\n"); | ||
134 | } | ||
135 | if ((err = snd_card_register(card)) < 0) { | ||
136 | snd_card_free(card); | ||
137 | return err; | ||
138 | } | ||
139 | snd_cs4231_cards[dev] = card; | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static int __init alsa_card_cs4231_init(void) | ||
144 | { | ||
145 | int dev, cards; | ||
146 | |||
147 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
148 | if (snd_card_cs4231_probe(dev) >= 0) | ||
149 | cards++; | ||
150 | } | ||
151 | if (!cards) { | ||
152 | #ifdef MODULE | ||
153 | printk(KERN_ERR "CS4231 soundcard not found or device busy\n"); | ||
154 | #endif | ||
155 | return -ENODEV; | ||
156 | } | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static void __exit alsa_card_cs4231_exit(void) | ||
161 | { | ||
162 | int idx; | ||
163 | |||
164 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
165 | snd_card_free(snd_cs4231_cards[idx]); | ||
166 | } | ||
167 | |||
168 | module_init(alsa_card_cs4231_init) | ||
169 | module_exit(alsa_card_cs4231_exit) | ||
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c new file mode 100644 index 000000000000..3e7a2a33a5ca --- /dev/null +++ b/sound/isa/cs423x/cs4231_lib.c | |||
@@ -0,0 +1,1964 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips | ||
4 | * | ||
5 | * Bugs: | ||
6 | * - sometimes record brokes playback with WSS portion of | ||
7 | * Yamaha OPL3-SA3 chip | ||
8 | * - CS4231 (GUS MAX) - still trouble with occasional noises | ||
9 | * - broken initialization? | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <sound/driver.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/pm.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/ioport.h> | ||
34 | #include <sound/core.h> | ||
35 | #include <sound/cs4231.h> | ||
36 | #include <sound/pcm_params.h> | ||
37 | |||
38 | #include <asm/io.h> | ||
39 | #include <asm/dma.h> | ||
40 | #include <asm/irq.h> | ||
41 | |||
42 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
43 | MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | |||
46 | #if 0 | ||
47 | #define SNDRV_DEBUG_MCE | ||
48 | #endif | ||
49 | |||
50 | /* | ||
51 | * Some variables | ||
52 | */ | ||
53 | |||
54 | static unsigned char freq_bits[14] = { | ||
55 | /* 5510 */ 0x00 | CS4231_XTAL2, | ||
56 | /* 6620 */ 0x0E | CS4231_XTAL2, | ||
57 | /* 8000 */ 0x00 | CS4231_XTAL1, | ||
58 | /* 9600 */ 0x0E | CS4231_XTAL1, | ||
59 | /* 11025 */ 0x02 | CS4231_XTAL2, | ||
60 | /* 16000 */ 0x02 | CS4231_XTAL1, | ||
61 | /* 18900 */ 0x04 | CS4231_XTAL2, | ||
62 | /* 22050 */ 0x06 | CS4231_XTAL2, | ||
63 | /* 27042 */ 0x04 | CS4231_XTAL1, | ||
64 | /* 32000 */ 0x06 | CS4231_XTAL1, | ||
65 | /* 33075 */ 0x0C | CS4231_XTAL2, | ||
66 | /* 37800 */ 0x08 | CS4231_XTAL2, | ||
67 | /* 44100 */ 0x0A | CS4231_XTAL2, | ||
68 | /* 48000 */ 0x0C | CS4231_XTAL1 | ||
69 | }; | ||
70 | |||
71 | static unsigned int rates[14] = { | ||
72 | 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, | ||
73 | 27042, 32000, 33075, 37800, 44100, 48000 | ||
74 | }; | ||
75 | |||
76 | static snd_pcm_hw_constraint_list_t hw_constraints_rates = { | ||
77 | .count = 14, | ||
78 | .list = rates, | ||
79 | .mask = 0, | ||
80 | }; | ||
81 | |||
82 | static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime) | ||
83 | { | ||
84 | return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
85 | } | ||
86 | |||
87 | static unsigned char snd_cs4231_original_image[32] = | ||
88 | { | ||
89 | 0x00, /* 00/00 - lic */ | ||
90 | 0x00, /* 01/01 - ric */ | ||
91 | 0x9f, /* 02/02 - la1ic */ | ||
92 | 0x9f, /* 03/03 - ra1ic */ | ||
93 | 0x9f, /* 04/04 - la2ic */ | ||
94 | 0x9f, /* 05/05 - ra2ic */ | ||
95 | 0xbf, /* 06/06 - loc */ | ||
96 | 0xbf, /* 07/07 - roc */ | ||
97 | 0x20, /* 08/08 - pdfr */ | ||
98 | CS4231_AUTOCALIB, /* 09/09 - ic */ | ||
99 | 0x00, /* 0a/10 - pc */ | ||
100 | 0x00, /* 0b/11 - ti */ | ||
101 | CS4231_MODE2, /* 0c/12 - mi */ | ||
102 | 0xfc, /* 0d/13 - lbc */ | ||
103 | 0x00, /* 0e/14 - pbru */ | ||
104 | 0x00, /* 0f/15 - pbrl */ | ||
105 | 0x80, /* 10/16 - afei */ | ||
106 | 0x01, /* 11/17 - afeii */ | ||
107 | 0x9f, /* 12/18 - llic */ | ||
108 | 0x9f, /* 13/19 - rlic */ | ||
109 | 0x00, /* 14/20 - tlb */ | ||
110 | 0x00, /* 15/21 - thb */ | ||
111 | 0x00, /* 16/22 - la3mic/reserved */ | ||
112 | 0x00, /* 17/23 - ra3mic/reserved */ | ||
113 | 0x00, /* 18/24 - afs */ | ||
114 | 0x00, /* 19/25 - lamoc/version */ | ||
115 | 0xcf, /* 1a/26 - mioc */ | ||
116 | 0x00, /* 1b/27 - ramoc/reserved */ | ||
117 | 0x20, /* 1c/28 - cdfr */ | ||
118 | 0x00, /* 1d/29 - res4 */ | ||
119 | 0x00, /* 1e/30 - cbru */ | ||
120 | 0x00, /* 1f/31 - cbrl */ | ||
121 | }; | ||
122 | |||
123 | /* | ||
124 | * Basic I/O functions | ||
125 | */ | ||
126 | |||
127 | #if !defined(EBUS_SUPPORT) && !defined(SBUS_SUPPORT) | ||
128 | #define __CS4231_INLINE__ inline | ||
129 | #else | ||
130 | #define __CS4231_INLINE__ /* nothing */ | ||
131 | #endif | ||
132 | |||
133 | static __CS4231_INLINE__ void cs4231_outb(cs4231_t *chip, u8 offset, u8 val) | ||
134 | { | ||
135 | #ifdef EBUS_SUPPORT | ||
136 | if (chip->ebus->flag) { | ||
137 | writeb(val, chip->port + (offset << 2)); | ||
138 | } else { | ||
139 | #endif | ||
140 | #ifdef SBUS_SUPPORT | ||
141 | sbus_writeb(val, chip->port + (offset << 2)); | ||
142 | #endif | ||
143 | #ifdef EBUS_SUPPORT | ||
144 | } | ||
145 | #endif | ||
146 | #ifdef LEGACY_SUPPORT | ||
147 | outb(val, chip->port + offset); | ||
148 | #endif | ||
149 | } | ||
150 | |||
151 | static __CS4231_INLINE__ u8 cs4231_inb(cs4231_t *chip, u8 offset) | ||
152 | { | ||
153 | #ifdef EBUS_SUPPORT | ||
154 | if (chip->ebus_flag) { | ||
155 | return readb(chip->port + (offset << 2)); | ||
156 | } else { | ||
157 | #endif | ||
158 | #ifdef SBUS_SUPPORT | ||
159 | return sbus_readb(chip->port + (offset << 2)); | ||
160 | #endif | ||
161 | #ifdef EBUS_SUPPORT | ||
162 | } | ||
163 | #endif | ||
164 | #ifdef LEGACY_SUPPORT | ||
165 | return inb(chip->port + offset); | ||
166 | #endif | ||
167 | } | ||
168 | |||
169 | static void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, | ||
170 | unsigned char mask, unsigned char value) | ||
171 | { | ||
172 | int timeout; | ||
173 | unsigned char tmp; | ||
174 | |||
175 | for (timeout = 250; | ||
176 | timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); | ||
177 | timeout--) | ||
178 | udelay(100); | ||
179 | #ifdef CONFIG_SND_DEBUG | ||
180 | if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) | ||
181 | snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); | ||
182 | #endif | ||
183 | if (chip->calibrate_mute) { | ||
184 | chip->image[reg] &= mask; | ||
185 | chip->image[reg] |= value; | ||
186 | } else { | ||
187 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); | ||
188 | mb(); | ||
189 | tmp = (chip->image[reg] & mask) | value; | ||
190 | cs4231_outb(chip, CS4231P(REG), tmp); | ||
191 | chip->image[reg] = tmp; | ||
192 | mb(); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value) | ||
197 | { | ||
198 | int timeout; | ||
199 | |||
200 | for (timeout = 250; | ||
201 | timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); | ||
202 | timeout--) | ||
203 | udelay(10); | ||
204 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); | ||
205 | cs4231_outb(chip, CS4231P(REG), value); | ||
206 | mb(); | ||
207 | } | ||
208 | |||
209 | void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value) | ||
210 | { | ||
211 | int timeout; | ||
212 | |||
213 | for (timeout = 250; | ||
214 | timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); | ||
215 | timeout--) | ||
216 | udelay(100); | ||
217 | #ifdef CONFIG_SND_DEBUG | ||
218 | if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) | ||
219 | snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); | ||
220 | #endif | ||
221 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); | ||
222 | cs4231_outb(chip, CS4231P(REG), value); | ||
223 | chip->image[reg] = value; | ||
224 | mb(); | ||
225 | #if 0 | ||
226 | printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); | ||
227 | #endif | ||
228 | } | ||
229 | |||
230 | unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) | ||
231 | { | ||
232 | int timeout; | ||
233 | |||
234 | for (timeout = 250; | ||
235 | timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); | ||
236 | timeout--) | ||
237 | udelay(100); | ||
238 | #ifdef CONFIG_SND_DEBUG | ||
239 | if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) | ||
240 | snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); | ||
241 | #endif | ||
242 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); | ||
243 | mb(); | ||
244 | return cs4231_inb(chip, CS4231P(REG)); | ||
245 | } | ||
246 | |||
247 | void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val) | ||
248 | { | ||
249 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); | ||
250 | cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01)); | ||
251 | cs4231_outb(chip, CS4231P(REG), val); | ||
252 | chip->eimage[CS4236_REG(reg)] = val; | ||
253 | #if 0 | ||
254 | printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val); | ||
255 | #endif | ||
256 | } | ||
257 | |||
258 | unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg) | ||
259 | { | ||
260 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); | ||
261 | cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01)); | ||
262 | #if 1 | ||
263 | return cs4231_inb(chip, CS4231P(REG)); | ||
264 | #else | ||
265 | { | ||
266 | unsigned char res; | ||
267 | res = cs4231_inb(chip, CS4231P(REG)); | ||
268 | printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res); | ||
269 | return res; | ||
270 | } | ||
271 | #endif | ||
272 | } | ||
273 | |||
274 | #if 0 | ||
275 | |||
276 | static void snd_cs4231_debug(cs4231_t *chip) | ||
277 | { | ||
278 | printk("CS4231 REGS: INDEX = 0x%02x ", cs4231_inb(chip, CS4231P(REGSEL))); | ||
279 | printk(" STATUS = 0x%02x\n", cs4231_inb(chip, CS4231P(STATUS))); | ||
280 | printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00)); | ||
281 | printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10)); | ||
282 | printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01)); | ||
283 | printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11)); | ||
284 | printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02)); | ||
285 | printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12)); | ||
286 | printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03)); | ||
287 | printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13)); | ||
288 | printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04)); | ||
289 | printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14)); | ||
290 | printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05)); | ||
291 | printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15)); | ||
292 | printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06)); | ||
293 | printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16)); | ||
294 | printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07)); | ||
295 | printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); | ||
296 | printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08)); | ||
297 | printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18)); | ||
298 | printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09)); | ||
299 | printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19)); | ||
300 | printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a)); | ||
301 | printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); | ||
302 | printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b)); | ||
303 | printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); | ||
304 | printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c)); | ||
305 | printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); | ||
306 | printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d)); | ||
307 | printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); | ||
308 | printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e)); | ||
309 | printk(" 0x1e: ply lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); | ||
310 | printk(" 0x0f: rec upr count = 0x%02x ", snd_cs4231_in(chip, 0x0f)); | ||
311 | printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); | ||
312 | } | ||
313 | |||
314 | #endif | ||
315 | |||
316 | /* | ||
317 | * CS4231 detection / MCE routines | ||
318 | */ | ||
319 | |||
320 | static void snd_cs4231_busy_wait(cs4231_t *chip) | ||
321 | { | ||
322 | int timeout; | ||
323 | |||
324 | /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ | ||
325 | for (timeout = 5; timeout > 0; timeout--) | ||
326 | cs4231_inb(chip, CS4231P(REGSEL)); | ||
327 | /* end of cleanup sequence */ | ||
328 | for (timeout = 250; | ||
329 | timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); | ||
330 | timeout--) | ||
331 | udelay(10); | ||
332 | } | ||
333 | |||
334 | void snd_cs4231_mce_up(cs4231_t *chip) | ||
335 | { | ||
336 | unsigned long flags; | ||
337 | int timeout; | ||
338 | |||
339 | for (timeout = 250; timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); timeout--) | ||
340 | udelay(100); | ||
341 | #ifdef CONFIG_SND_DEBUG | ||
342 | if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) | ||
343 | snd_printk("mce_up - auto calibration time out (0)\n"); | ||
344 | #endif | ||
345 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
346 | chip->mce_bit |= CS4231_MCE; | ||
347 | timeout = cs4231_inb(chip, CS4231P(REGSEL)); | ||
348 | if (timeout == 0x80) | ||
349 | snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
350 | if (!(timeout & CS4231_MCE)) | ||
351 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); | ||
352 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
353 | } | ||
354 | |||
355 | void snd_cs4231_mce_down(cs4231_t *chip) | ||
356 | { | ||
357 | unsigned long flags; | ||
358 | int timeout; | ||
359 | |||
360 | snd_cs4231_busy_wait(chip); | ||
361 | #if 0 | ||
362 | printk("(1) timeout = %i\n", timeout); | ||
363 | #endif | ||
364 | #ifdef CONFIG_SND_DEBUG | ||
365 | if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) | ||
366 | snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL)); | ||
367 | #endif | ||
368 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
369 | chip->mce_bit &= ~CS4231_MCE; | ||
370 | timeout = cs4231_inb(chip, CS4231P(REGSEL)); | ||
371 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); | ||
372 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
373 | if (timeout == 0x80) | ||
374 | snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
375 | if ((timeout & CS4231_MCE) == 0 || | ||
376 | !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { | ||
377 | return; | ||
378 | } | ||
379 | snd_cs4231_busy_wait(chip); | ||
380 | |||
381 | /* calibration process */ | ||
382 | |||
383 | for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--) | ||
384 | udelay(10); | ||
385 | if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { | ||
386 | snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); | ||
387 | return; | ||
388 | } | ||
389 | #if 0 | ||
390 | printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); | ||
391 | #endif | ||
392 | /* in 10 ms increments, check condition, up to 250 ms */ | ||
393 | timeout = 25; | ||
394 | while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { | ||
395 | if (--timeout < 0) { | ||
396 | snd_printk("mce_down - auto calibration time out (2)\n"); | ||
397 | return; | ||
398 | } | ||
399 | msleep(10); | ||
400 | } | ||
401 | #if 0 | ||
402 | printk("(3) jiffies = %li\n", jiffies); | ||
403 | #endif | ||
404 | /* in 10 ms increments, check condition, up to 100 ms */ | ||
405 | timeout = 10; | ||
406 | while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { | ||
407 | if (--timeout < 0) { | ||
408 | snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); | ||
409 | return; | ||
410 | } | ||
411 | msleep(10); | ||
412 | } | ||
413 | #if 0 | ||
414 | printk("(4) jiffies = %li\n", jiffies); | ||
415 | snd_printk("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL))); | ||
416 | #endif | ||
417 | } | ||
418 | |||
419 | static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) | ||
420 | { | ||
421 | switch (format & 0xe0) { | ||
422 | case CS4231_LINEAR_16: | ||
423 | case CS4231_LINEAR_16_BIG: | ||
424 | size >>= 1; | ||
425 | break; | ||
426 | case CS4231_ADPCM_16: | ||
427 | return size >> 2; | ||
428 | } | ||
429 | if (format & CS4231_STEREO) | ||
430 | size >>= 1; | ||
431 | return size; | ||
432 | } | ||
433 | |||
434 | static int snd_cs4231_trigger(snd_pcm_substream_t *substream, | ||
435 | int cmd) | ||
436 | { | ||
437 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
438 | int result = 0; | ||
439 | unsigned int what; | ||
440 | struct list_head *pos; | ||
441 | snd_pcm_substream_t *s; | ||
442 | int do_start; | ||
443 | |||
444 | #if 0 | ||
445 | printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, cs4231_inb(chip, CS4231P(STATUS))); | ||
446 | #endif | ||
447 | |||
448 | switch (cmd) { | ||
449 | case SNDRV_PCM_TRIGGER_START: | ||
450 | case SNDRV_PCM_TRIGGER_RESUME: | ||
451 | do_start = 1; break; | ||
452 | case SNDRV_PCM_TRIGGER_STOP: | ||
453 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
454 | do_start = 0; break; | ||
455 | default: | ||
456 | return -EINVAL; | ||
457 | } | ||
458 | |||
459 | what = 0; | ||
460 | snd_pcm_group_for_each(pos, substream) { | ||
461 | s = snd_pcm_group_substream_entry(pos); | ||
462 | if (s == chip->playback_substream) { | ||
463 | what |= CS4231_PLAYBACK_ENABLE; | ||
464 | snd_pcm_trigger_done(s, substream); | ||
465 | } else if (s == chip->capture_substream) { | ||
466 | what |= CS4231_RECORD_ENABLE; | ||
467 | snd_pcm_trigger_done(s, substream); | ||
468 | } | ||
469 | } | ||
470 | spin_lock(&chip->reg_lock); | ||
471 | if (do_start) { | ||
472 | chip->image[CS4231_IFACE_CTRL] |= what; | ||
473 | if (chip->trigger) | ||
474 | chip->trigger(chip, what, 1); | ||
475 | } else { | ||
476 | chip->image[CS4231_IFACE_CTRL] &= ~what; | ||
477 | if (chip->trigger) | ||
478 | chip->trigger(chip, what, 0); | ||
479 | } | ||
480 | snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); | ||
481 | spin_unlock(&chip->reg_lock); | ||
482 | #if 0 | ||
483 | snd_cs4231_debug(chip); | ||
484 | #endif | ||
485 | return result; | ||
486 | } | ||
487 | |||
488 | /* | ||
489 | * CODEC I/O | ||
490 | */ | ||
491 | |||
492 | static unsigned char snd_cs4231_get_rate(unsigned int rate) | ||
493 | { | ||
494 | int i; | ||
495 | |||
496 | for (i = 0; i < 14; i++) | ||
497 | if (rate == rates[i]) | ||
498 | return freq_bits[i]; | ||
499 | // snd_BUG(); | ||
500 | return freq_bits[13]; | ||
501 | } | ||
502 | |||
503 | static unsigned char snd_cs4231_get_format(cs4231_t *chip, | ||
504 | int format, | ||
505 | int channels) | ||
506 | { | ||
507 | unsigned char rformat; | ||
508 | |||
509 | rformat = CS4231_LINEAR_8; | ||
510 | switch (format) { | ||
511 | case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break; | ||
512 | case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break; | ||
513 | case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break; | ||
514 | case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break; | ||
515 | case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break; | ||
516 | } | ||
517 | if (channels > 1) | ||
518 | rformat |= CS4231_STEREO; | ||
519 | #if 0 | ||
520 | snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); | ||
521 | #endif | ||
522 | return rformat; | ||
523 | } | ||
524 | |||
525 | static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute) | ||
526 | { | ||
527 | unsigned long flags; | ||
528 | |||
529 | mute = mute ? 1 : 0; | ||
530 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
531 | if (chip->calibrate_mute == mute) { | ||
532 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
533 | return; | ||
534 | } | ||
535 | if (!mute) { | ||
536 | snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]); | ||
537 | snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]); | ||
538 | snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]); | ||
539 | } | ||
540 | snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); | ||
541 | snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); | ||
542 | snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); | ||
543 | snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); | ||
544 | snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); | ||
545 | snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); | ||
546 | snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); | ||
547 | snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); | ||
548 | snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); | ||
549 | if (chip->hardware == CS4231_HW_INTERWAVE) { | ||
550 | snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]); | ||
551 | snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]); | ||
552 | snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]); | ||
553 | snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]); | ||
554 | } | ||
555 | chip->calibrate_mute = mute; | ||
556 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
557 | } | ||
558 | |||
559 | static void snd_cs4231_playback_format(cs4231_t *chip, | ||
560 | snd_pcm_hw_params_t *params, | ||
561 | unsigned char pdfr) | ||
562 | { | ||
563 | unsigned long flags; | ||
564 | int full_calib = 1; | ||
565 | |||
566 | down(&chip->mce_mutex); | ||
567 | snd_cs4231_calibrate_mute(chip, 1); | ||
568 | if (chip->hardware == CS4231_HW_CS4231A || | ||
569 | (chip->hardware & CS4231_HW_CS4232_MASK)) { | ||
570 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
571 | if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */ | ||
572 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); | ||
573 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); | ||
574 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); | ||
575 | udelay(100); /* Fixes audible clicks at least on GUS MAX */ | ||
576 | full_calib = 0; | ||
577 | } | ||
578 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
579 | } | ||
580 | if (full_calib) { | ||
581 | snd_cs4231_mce_up(chip); | ||
582 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
583 | if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) { | ||
584 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, | ||
585 | (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? | ||
586 | (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : | ||
587 | pdfr); | ||
588 | } else { | ||
589 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); | ||
590 | } | ||
591 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
592 | snd_cs4231_mce_down(chip); | ||
593 | } | ||
594 | snd_cs4231_calibrate_mute(chip, 0); | ||
595 | up(&chip->mce_mutex); | ||
596 | } | ||
597 | |||
598 | static void snd_cs4231_capture_format(cs4231_t *chip, | ||
599 | snd_pcm_hw_params_t *params, | ||
600 | unsigned char cdfr) | ||
601 | { | ||
602 | unsigned long flags; | ||
603 | int full_calib = 1; | ||
604 | |||
605 | down(&chip->mce_mutex); | ||
606 | snd_cs4231_calibrate_mute(chip, 1); | ||
607 | if (chip->hardware == CS4231_HW_CS4231A || | ||
608 | (chip->hardware & CS4231_HW_CS4232_MASK)) { | ||
609 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
610 | if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */ | ||
611 | (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { | ||
612 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); | ||
613 | snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr); | ||
614 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); | ||
615 | full_calib = 0; | ||
616 | } | ||
617 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
618 | } | ||
619 | if (full_calib) { | ||
620 | snd_cs4231_mce_up(chip); | ||
621 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
622 | if (chip->hardware != CS4231_HW_INTERWAVE) { | ||
623 | if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { | ||
624 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, | ||
625 | ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | | ||
626 | (cdfr & 0x0f)); | ||
627 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
628 | snd_cs4231_mce_down(chip); | ||
629 | snd_cs4231_mce_up(chip); | ||
630 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
631 | } | ||
632 | } | ||
633 | snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); | ||
634 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
635 | snd_cs4231_mce_down(chip); | ||
636 | } | ||
637 | snd_cs4231_calibrate_mute(chip, 0); | ||
638 | up(&chip->mce_mutex); | ||
639 | } | ||
640 | |||
641 | /* | ||
642 | * Timer interface | ||
643 | */ | ||
644 | |||
645 | static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer) | ||
646 | { | ||
647 | cs4231_t *chip = snd_timer_chip(timer); | ||
648 | if (chip->hardware & CS4231_HW_CS4236B_MASK) | ||
649 | return 14467; | ||
650 | else | ||
651 | return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; | ||
652 | } | ||
653 | |||
654 | static int snd_cs4231_timer_start(snd_timer_t * timer) | ||
655 | { | ||
656 | unsigned long flags; | ||
657 | unsigned int ticks; | ||
658 | cs4231_t *chip = snd_timer_chip(timer); | ||
659 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
660 | ticks = timer->sticks; | ||
661 | if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || | ||
662 | (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || | ||
663 | (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { | ||
664 | snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8)); | ||
665 | snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks); | ||
666 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); | ||
667 | } | ||
668 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int snd_cs4231_timer_stop(snd_timer_t * timer) | ||
673 | { | ||
674 | unsigned long flags; | ||
675 | cs4231_t *chip = snd_timer_chip(timer); | ||
676 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
677 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); | ||
678 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | static void snd_cs4231_init(cs4231_t *chip) | ||
683 | { | ||
684 | unsigned long flags; | ||
685 | |||
686 | snd_cs4231_mce_down(chip); | ||
687 | |||
688 | #ifdef SNDRV_DEBUG_MCE | ||
689 | snd_printk("init: (1)\n"); | ||
690 | #endif | ||
691 | snd_cs4231_mce_up(chip); | ||
692 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
693 | chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | | ||
694 | CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | | ||
695 | CS4231_CALIB_MODE); | ||
696 | chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; | ||
697 | snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); | ||
698 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
699 | snd_cs4231_mce_down(chip); | ||
700 | |||
701 | #ifdef SNDRV_DEBUG_MCE | ||
702 | snd_printk("init: (2)\n"); | ||
703 | #endif | ||
704 | |||
705 | snd_cs4231_mce_up(chip); | ||
706 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
707 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); | ||
708 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
709 | snd_cs4231_mce_down(chip); | ||
710 | |||
711 | #ifdef SNDRV_DEBUG_MCE | ||
712 | snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); | ||
713 | #endif | ||
714 | |||
715 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
716 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); | ||
717 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
718 | |||
719 | snd_cs4231_mce_up(chip); | ||
720 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
721 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); | ||
722 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
723 | snd_cs4231_mce_down(chip); | ||
724 | |||
725 | #ifdef SNDRV_DEBUG_MCE | ||
726 | snd_printk("init: (4)\n"); | ||
727 | #endif | ||
728 | |||
729 | snd_cs4231_mce_up(chip); | ||
730 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
731 | snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); | ||
732 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
733 | snd_cs4231_mce_down(chip); | ||
734 | |||
735 | #ifdef SNDRV_DEBUG_MCE | ||
736 | snd_printk("init: (5)\n"); | ||
737 | #endif | ||
738 | } | ||
739 | |||
740 | static int snd_cs4231_open(cs4231_t *chip, unsigned int mode) | ||
741 | { | ||
742 | unsigned long flags; | ||
743 | |||
744 | down(&chip->open_mutex); | ||
745 | if ((chip->mode & mode) || | ||
746 | ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) { | ||
747 | up(&chip->open_mutex); | ||
748 | return -EAGAIN; | ||
749 | } | ||
750 | if (chip->mode & CS4231_MODE_OPEN) { | ||
751 | chip->mode |= mode; | ||
752 | up(&chip->open_mutex); | ||
753 | return 0; | ||
754 | } | ||
755 | /* ok. now enable and ack CODEC IRQ */ | ||
756 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
757 | snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | | ||
758 | CS4231_RECORD_IRQ | | ||
759 | CS4231_TIMER_IRQ); | ||
760 | snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); | ||
761 | cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ | ||
762 | cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ | ||
763 | chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; | ||
764 | snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); | ||
765 | snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | | ||
766 | CS4231_RECORD_IRQ | | ||
767 | CS4231_TIMER_IRQ); | ||
768 | snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); | ||
769 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
770 | |||
771 | chip->mode = mode; | ||
772 | up(&chip->open_mutex); | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static void snd_cs4231_close(cs4231_t *chip, unsigned int mode) | ||
777 | { | ||
778 | unsigned long flags; | ||
779 | |||
780 | down(&chip->open_mutex); | ||
781 | chip->mode &= ~mode; | ||
782 | if (chip->mode & CS4231_MODE_OPEN) { | ||
783 | up(&chip->open_mutex); | ||
784 | return; | ||
785 | } | ||
786 | snd_cs4231_calibrate_mute(chip, 1); | ||
787 | |||
788 | /* disable IRQ */ | ||
789 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
790 | snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); | ||
791 | cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ | ||
792 | cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ | ||
793 | chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; | ||
794 | snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); | ||
795 | |||
796 | /* now disable record & playback */ | ||
797 | |||
798 | if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | | ||
799 | CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { | ||
800 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
801 | snd_cs4231_mce_up(chip); | ||
802 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
803 | chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | | ||
804 | CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); | ||
805 | snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); | ||
806 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
807 | snd_cs4231_mce_down(chip); | ||
808 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
809 | } | ||
810 | |||
811 | /* clear IRQ again */ | ||
812 | snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); | ||
813 | cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ | ||
814 | cs4231_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */ | ||
815 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
816 | |||
817 | snd_cs4231_calibrate_mute(chip, 0); | ||
818 | |||
819 | chip->mode = 0; | ||
820 | up(&chip->open_mutex); | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * timer open/close | ||
825 | */ | ||
826 | |||
827 | static int snd_cs4231_timer_open(snd_timer_t * timer) | ||
828 | { | ||
829 | cs4231_t *chip = snd_timer_chip(timer); | ||
830 | snd_cs4231_open(chip, CS4231_MODE_TIMER); | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | static int snd_cs4231_timer_close(snd_timer_t * timer) | ||
835 | { | ||
836 | cs4231_t *chip = snd_timer_chip(timer); | ||
837 | snd_cs4231_close(chip, CS4231_MODE_TIMER); | ||
838 | return 0; | ||
839 | } | ||
840 | |||
841 | static struct _snd_timer_hardware snd_cs4231_timer_table = | ||
842 | { | ||
843 | .flags = SNDRV_TIMER_HW_AUTO, | ||
844 | .resolution = 9945, | ||
845 | .ticks = 65535, | ||
846 | .open = snd_cs4231_timer_open, | ||
847 | .close = snd_cs4231_timer_close, | ||
848 | .c_resolution = snd_cs4231_timer_resolution, | ||
849 | .start = snd_cs4231_timer_start, | ||
850 | .stop = snd_cs4231_timer_stop, | ||
851 | }; | ||
852 | |||
853 | /* | ||
854 | * ok.. exported functions.. | ||
855 | */ | ||
856 | |||
857 | static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream, | ||
858 | snd_pcm_hw_params_t * hw_params) | ||
859 | { | ||
860 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
861 | unsigned char new_pdfr; | ||
862 | int err; | ||
863 | |||
864 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
865 | return err; | ||
866 | new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | | ||
867 | snd_cs4231_get_rate(params_rate(hw_params)); | ||
868 | chip->set_playback_format(chip, hw_params, new_pdfr); | ||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream) | ||
873 | { | ||
874 | return snd_pcm_lib_free_pages(substream); | ||
875 | } | ||
876 | |||
877 | #ifdef LEGACY_SUPPORT | ||
878 | static int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream) | ||
879 | { | ||
880 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
881 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
882 | unsigned long flags; | ||
883 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
884 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
885 | |||
886 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
887 | chip->p_dma_size = size; | ||
888 | chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); | ||
889 | snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
890 | count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; | ||
891 | snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); | ||
892 | snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); | ||
893 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
894 | #if 0 | ||
895 | snd_cs4231_debug(chip); | ||
896 | #endif | ||
897 | return 0; | ||
898 | } | ||
899 | #endif /* LEGACY_SUPPORT */ | ||
900 | |||
901 | static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream, | ||
902 | snd_pcm_hw_params_t * hw_params) | ||
903 | { | ||
904 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
905 | unsigned char new_cdfr; | ||
906 | int err; | ||
907 | |||
908 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
909 | return err; | ||
910 | new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | | ||
911 | snd_cs4231_get_rate(params_rate(hw_params)); | ||
912 | chip->set_capture_format(chip, hw_params, new_cdfr); | ||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream) | ||
917 | { | ||
918 | return snd_pcm_lib_free_pages(substream); | ||
919 | } | ||
920 | |||
921 | #ifdef LEGACY_SUPPORT | ||
922 | static int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream) | ||
923 | { | ||
924 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
925 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
926 | unsigned long flags; | ||
927 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
928 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
929 | |||
930 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
931 | chip->c_dma_size = size; | ||
932 | chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); | ||
933 | snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | ||
934 | count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1; | ||
935 | if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { | ||
936 | snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); | ||
937 | snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); | ||
938 | } else { | ||
939 | snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); | ||
940 | snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8)); | ||
941 | } | ||
942 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
943 | return 0; | ||
944 | } | ||
945 | #endif | ||
946 | |||
947 | static void snd_cs4231_overrange(cs4231_t *chip) | ||
948 | { | ||
949 | unsigned long flags; | ||
950 | unsigned char res; | ||
951 | |||
952 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
953 | res = snd_cs4231_in(chip, CS4231_TEST_INIT); | ||
954 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
955 | if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */ | ||
956 | chip->capture_substream->runtime->overrange++; | ||
957 | } | ||
958 | |||
959 | irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
960 | { | ||
961 | cs4231_t *chip = dev_id; | ||
962 | unsigned char status; | ||
963 | |||
964 | status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); | ||
965 | if (status & CS4231_TIMER_IRQ) { | ||
966 | if (chip->timer) | ||
967 | snd_timer_interrupt(chip->timer, chip->timer->sticks); | ||
968 | } | ||
969 | if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { | ||
970 | if (status & CS4231_PLAYBACK_IRQ) { | ||
971 | if (chip->mode & CS4231_MODE_PLAY) { | ||
972 | if (chip->playback_substream) | ||
973 | snd_pcm_period_elapsed(chip->playback_substream); | ||
974 | } | ||
975 | if (chip->mode & CS4231_MODE_RECORD) { | ||
976 | if (chip->capture_substream) { | ||
977 | snd_cs4231_overrange(chip); | ||
978 | snd_pcm_period_elapsed(chip->capture_substream); | ||
979 | } | ||
980 | } | ||
981 | } | ||
982 | } else { | ||
983 | if (status & CS4231_PLAYBACK_IRQ) { | ||
984 | if (chip->playback_substream) | ||
985 | snd_pcm_period_elapsed(chip->playback_substream); | ||
986 | } | ||
987 | if (status & CS4231_RECORD_IRQ) { | ||
988 | if (chip->capture_substream) { | ||
989 | snd_cs4231_overrange(chip); | ||
990 | snd_pcm_period_elapsed(chip->capture_substream); | ||
991 | } | ||
992 | } | ||
993 | } | ||
994 | |||
995 | spin_lock(&chip->reg_lock); | ||
996 | snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); | ||
997 | spin_unlock(&chip->reg_lock); | ||
998 | return IRQ_HANDLED; | ||
999 | } | ||
1000 | |||
1001 | #ifdef LEGACY_SUPPORT | ||
1002 | static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream) | ||
1003 | { | ||
1004 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
1005 | size_t ptr; | ||
1006 | |||
1007 | if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) | ||
1008 | return 0; | ||
1009 | ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); | ||
1010 | return bytes_to_frames(substream->runtime, ptr); | ||
1011 | } | ||
1012 | |||
1013 | static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream) | ||
1014 | { | ||
1015 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
1016 | size_t ptr; | ||
1017 | |||
1018 | if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) | ||
1019 | return 0; | ||
1020 | ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); | ||
1021 | return bytes_to_frames(substream->runtime, ptr); | ||
1022 | } | ||
1023 | #endif /* LEGACY_SUPPORT */ | ||
1024 | |||
1025 | /* | ||
1026 | |||
1027 | */ | ||
1028 | |||
1029 | static int snd_cs4231_probe(cs4231_t *chip) | ||
1030 | { | ||
1031 | unsigned long flags; | ||
1032 | int i, id, rev; | ||
1033 | unsigned char *ptr; | ||
1034 | unsigned int hw; | ||
1035 | |||
1036 | #if 0 | ||
1037 | snd_cs4231_debug(chip); | ||
1038 | #endif | ||
1039 | id = 0; | ||
1040 | for (i = 0; i < 50; i++) { | ||
1041 | mb(); | ||
1042 | if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) | ||
1043 | udelay(2000); | ||
1044 | else { | ||
1045 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1046 | snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); | ||
1047 | id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; | ||
1048 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1049 | if (id == 0x0a) | ||
1050 | break; /* this is valid value */ | ||
1051 | } | ||
1052 | } | ||
1053 | snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); | ||
1054 | if (id != 0x0a) | ||
1055 | return -ENODEV; /* no valid device found */ | ||
1056 | |||
1057 | if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { | ||
1058 | rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7; | ||
1059 | snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); | ||
1060 | if (rev == 0x80) { | ||
1061 | unsigned char tmp = snd_cs4231_in(chip, 23); | ||
1062 | snd_cs4231_out(chip, 23, ~tmp); | ||
1063 | if (snd_cs4231_in(chip, 23) != tmp) | ||
1064 | chip->hardware = CS4231_HW_AD1845; | ||
1065 | else | ||
1066 | chip->hardware = CS4231_HW_CS4231; | ||
1067 | } else if (rev == 0xa0) { | ||
1068 | chip->hardware = CS4231_HW_CS4231A; | ||
1069 | } else if (rev == 0xa2) { | ||
1070 | chip->hardware = CS4231_HW_CS4232; | ||
1071 | } else if (rev == 0xb2) { | ||
1072 | chip->hardware = CS4231_HW_CS4232A; | ||
1073 | } else if (rev == 0x83) { | ||
1074 | chip->hardware = CS4231_HW_CS4236; | ||
1075 | } else if (rev == 0x03) { | ||
1076 | chip->hardware = CS4231_HW_CS4236B; | ||
1077 | } else { | ||
1078 | snd_printk("unknown CS chip with version 0x%x\n", rev); | ||
1079 | return -ENODEV; /* unknown CS4231 chip? */ | ||
1080 | } | ||
1081 | } | ||
1082 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1083 | cs4231_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */ | ||
1084 | cs4231_outb(chip, CS4231P(STATUS), 0); | ||
1085 | mb(); | ||
1086 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1087 | |||
1088 | chip->image[CS4231_MISC_INFO] = CS4231_MODE2; | ||
1089 | switch (chip->hardware) { | ||
1090 | case CS4231_HW_INTERWAVE: | ||
1091 | chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; | ||
1092 | break; | ||
1093 | case CS4231_HW_CS4235: | ||
1094 | case CS4231_HW_CS4236B: | ||
1095 | case CS4231_HW_CS4237B: | ||
1096 | case CS4231_HW_CS4238B: | ||
1097 | case CS4231_HW_CS4239: | ||
1098 | if (hw == CS4231_HW_DETECT3) | ||
1099 | chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; | ||
1100 | else | ||
1101 | chip->hardware = CS4231_HW_CS4236; | ||
1102 | break; | ||
1103 | } | ||
1104 | |||
1105 | chip->image[CS4231_IFACE_CTRL] = | ||
1106 | (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | | ||
1107 | (chip->single_dma ? CS4231_SINGLE_DMA : 0); | ||
1108 | chip->image[CS4231_ALT_FEATURE_1] = 0x80; | ||
1109 | chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; | ||
1110 | ptr = (unsigned char *) &chip->image; | ||
1111 | snd_cs4231_mce_down(chip); | ||
1112 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1113 | for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */ | ||
1114 | snd_cs4231_out(chip, i, *ptr++); | ||
1115 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1116 | snd_cs4231_mce_up(chip); | ||
1117 | snd_cs4231_mce_down(chip); | ||
1118 | |||
1119 | mdelay(2); | ||
1120 | |||
1121 | /* ok.. try check hardware version for CS4236+ chips */ | ||
1122 | if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { | ||
1123 | if (chip->hardware == CS4231_HW_CS4236B) { | ||
1124 | rev = snd_cs4236_ext_in(chip, CS4236_VERSION); | ||
1125 | snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); | ||
1126 | id = snd_cs4236_ext_in(chip, CS4236_VERSION); | ||
1127 | snd_cs4236_ext_out(chip, CS4236_VERSION, rev); | ||
1128 | snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); | ||
1129 | if ((id & 0x1f) == 0x1d) { /* CS4235 */ | ||
1130 | chip->hardware = CS4231_HW_CS4235; | ||
1131 | switch (id >> 5) { | ||
1132 | case 4: | ||
1133 | case 5: | ||
1134 | case 6: | ||
1135 | break; | ||
1136 | default: | ||
1137 | snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); | ||
1138 | } | ||
1139 | } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */ | ||
1140 | switch (id >> 5) { | ||
1141 | case 4: | ||
1142 | case 5: | ||
1143 | case 6: | ||
1144 | case 7: | ||
1145 | chip->hardware = CS4231_HW_CS4236B; | ||
1146 | break; | ||
1147 | default: | ||
1148 | snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); | ||
1149 | } | ||
1150 | } else if ((id & 0x1f) == 0x08) { /* CS4237B */ | ||
1151 | chip->hardware = CS4231_HW_CS4237B; | ||
1152 | switch (id >> 5) { | ||
1153 | case 4: | ||
1154 | case 5: | ||
1155 | case 6: | ||
1156 | case 7: | ||
1157 | break; | ||
1158 | default: | ||
1159 | snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); | ||
1160 | } | ||
1161 | } else if ((id & 0x1f) == 0x09) { /* CS4238B */ | ||
1162 | chip->hardware = CS4231_HW_CS4238B; | ||
1163 | switch (id >> 5) { | ||
1164 | case 5: | ||
1165 | case 6: | ||
1166 | case 7: | ||
1167 | break; | ||
1168 | default: | ||
1169 | snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); | ||
1170 | } | ||
1171 | } else if ((id & 0x1f) == 0x1e) { /* CS4239 */ | ||
1172 | chip->hardware = CS4231_HW_CS4239; | ||
1173 | switch (id >> 5) { | ||
1174 | case 4: | ||
1175 | case 5: | ||
1176 | case 6: | ||
1177 | break; | ||
1178 | default: | ||
1179 | snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); | ||
1180 | } | ||
1181 | } else { | ||
1182 | snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); | ||
1183 | } | ||
1184 | } | ||
1185 | } | ||
1186 | return 0; /* all things are ok.. */ | ||
1187 | } | ||
1188 | |||
1189 | /* | ||
1190 | |||
1191 | */ | ||
1192 | |||
1193 | static snd_pcm_hardware_t snd_cs4231_playback = | ||
1194 | { | ||
1195 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
1196 | SNDRV_PCM_INFO_MMAP_VALID | | ||
1197 | SNDRV_PCM_INFO_RESUME | | ||
1198 | SNDRV_PCM_INFO_SYNC_START), | ||
1199 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | | ||
1200 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), | ||
1201 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
1202 | .rate_min = 5510, | ||
1203 | .rate_max = 48000, | ||
1204 | .channels_min = 1, | ||
1205 | .channels_max = 2, | ||
1206 | .buffer_bytes_max = (128*1024), | ||
1207 | .period_bytes_min = 64, | ||
1208 | .period_bytes_max = (128*1024), | ||
1209 | .periods_min = 1, | ||
1210 | .periods_max = 1024, | ||
1211 | .fifo_size = 0, | ||
1212 | }; | ||
1213 | |||
1214 | static snd_pcm_hardware_t snd_cs4231_capture = | ||
1215 | { | ||
1216 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
1217 | SNDRV_PCM_INFO_MMAP_VALID | | ||
1218 | SNDRV_PCM_INFO_RESUME | | ||
1219 | SNDRV_PCM_INFO_SYNC_START), | ||
1220 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | | ||
1221 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), | ||
1222 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
1223 | .rate_min = 5510, | ||
1224 | .rate_max = 48000, | ||
1225 | .channels_min = 1, | ||
1226 | .channels_max = 2, | ||
1227 | .buffer_bytes_max = (128*1024), | ||
1228 | .period_bytes_min = 64, | ||
1229 | .period_bytes_max = (128*1024), | ||
1230 | .periods_min = 1, | ||
1231 | .periods_max = 1024, | ||
1232 | .fifo_size = 0, | ||
1233 | }; | ||
1234 | |||
1235 | /* | ||
1236 | |||
1237 | */ | ||
1238 | |||
1239 | static int snd_cs4231_playback_open(snd_pcm_substream_t * substream) | ||
1240 | { | ||
1241 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
1242 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1243 | int err; | ||
1244 | |||
1245 | runtime->hw = snd_cs4231_playback; | ||
1246 | |||
1247 | /* hardware bug in InterWave chipset */ | ||
1248 | if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3) | ||
1249 | runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; | ||
1250 | |||
1251 | /* hardware limitation of cheap chips */ | ||
1252 | if (chip->hardware == CS4231_HW_CS4235 || | ||
1253 | chip->hardware == CS4231_HW_CS4239) | ||
1254 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; | ||
1255 | |||
1256 | #ifdef LEGACY_SUPPORT | ||
1257 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); | ||
1258 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); | ||
1259 | |||
1260 | if (chip->claim_dma) { | ||
1261 | if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) | ||
1262 | return err; | ||
1263 | } | ||
1264 | #endif | ||
1265 | |||
1266 | if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) { | ||
1267 | #ifdef LEGACY_SUPPORT | ||
1268 | if (chip->release_dma) | ||
1269 | chip->release_dma(chip, chip->dma_private_data, chip->dma1); | ||
1270 | #endif | ||
1271 | snd_free_pages(runtime->dma_area, runtime->dma_bytes); | ||
1272 | return err; | ||
1273 | } | ||
1274 | chip->playback_substream = substream; | ||
1275 | #if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) | ||
1276 | chip->p_periods_sent = 0; | ||
1277 | #endif | ||
1278 | snd_pcm_set_sync(substream); | ||
1279 | chip->rate_constraint(runtime); | ||
1280 | return 0; | ||
1281 | } | ||
1282 | |||
1283 | static int snd_cs4231_capture_open(snd_pcm_substream_t * substream) | ||
1284 | { | ||
1285 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
1286 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1287 | int err; | ||
1288 | |||
1289 | runtime->hw = snd_cs4231_capture; | ||
1290 | |||
1291 | /* hardware limitation of cheap chips */ | ||
1292 | if (chip->hardware == CS4231_HW_CS4235 || | ||
1293 | chip->hardware == CS4231_HW_CS4239) | ||
1294 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; | ||
1295 | |||
1296 | #ifdef LEGACY_SUPPORT | ||
1297 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); | ||
1298 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); | ||
1299 | |||
1300 | if (chip->claim_dma) { | ||
1301 | if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) | ||
1302 | return err; | ||
1303 | } | ||
1304 | #endif | ||
1305 | |||
1306 | if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) { | ||
1307 | #ifdef LEGACY_SUPPORT | ||
1308 | if (chip->release_dma) | ||
1309 | chip->release_dma(chip, chip->dma_private_data, chip->dma2); | ||
1310 | #endif | ||
1311 | snd_free_pages(runtime->dma_area, runtime->dma_bytes); | ||
1312 | return err; | ||
1313 | } | ||
1314 | chip->capture_substream = substream; | ||
1315 | #if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) | ||
1316 | chip->c_periods_sent = 0; | ||
1317 | #endif | ||
1318 | snd_pcm_set_sync(substream); | ||
1319 | chip->rate_constraint(runtime); | ||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | static int snd_cs4231_playback_close(snd_pcm_substream_t * substream) | ||
1324 | { | ||
1325 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
1326 | |||
1327 | chip->playback_substream = NULL; | ||
1328 | snd_cs4231_close(chip, CS4231_MODE_PLAY); | ||
1329 | return 0; | ||
1330 | } | ||
1331 | |||
1332 | static int snd_cs4231_capture_close(snd_pcm_substream_t * substream) | ||
1333 | { | ||
1334 | cs4231_t *chip = snd_pcm_substream_chip(substream); | ||
1335 | |||
1336 | chip->capture_substream = NULL; | ||
1337 | snd_cs4231_close(chip, CS4231_MODE_RECORD); | ||
1338 | return 0; | ||
1339 | } | ||
1340 | |||
1341 | #ifdef CONFIG_PM | ||
1342 | |||
1343 | /* lowlevel suspend callback for CS4231 */ | ||
1344 | static void snd_cs4231_suspend(cs4231_t *chip) | ||
1345 | { | ||
1346 | int reg; | ||
1347 | unsigned long flags; | ||
1348 | |||
1349 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1350 | for (reg = 0; reg < 32; reg++) | ||
1351 | chip->image[reg] = snd_cs4231_in(chip, reg); | ||
1352 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1353 | } | ||
1354 | |||
1355 | /* lowlevel resume callback for CS4231 */ | ||
1356 | static void snd_cs4231_resume(cs4231_t *chip) | ||
1357 | { | ||
1358 | int reg; | ||
1359 | unsigned long flags; | ||
1360 | int timeout; | ||
1361 | |||
1362 | snd_cs4231_mce_up(chip); | ||
1363 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1364 | for (reg = 0; reg < 32; reg++) { | ||
1365 | switch (reg) { | ||
1366 | case CS4231_VERSION: | ||
1367 | break; | ||
1368 | default: | ||
1369 | snd_cs4231_out(chip, reg, chip->image[reg]); | ||
1370 | break; | ||
1371 | } | ||
1372 | } | ||
1373 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1374 | #if 0 | ||
1375 | snd_cs4231_mce_down(chip); | ||
1376 | #else | ||
1377 | /* The following is a workaround to avoid freeze after resume on TP600E. | ||
1378 | This is the first half of copy of snd_cs4231_mce_down(), but doesn't | ||
1379 | include rescheduling. -- iwai | ||
1380 | */ | ||
1381 | snd_cs4231_busy_wait(chip); | ||
1382 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1383 | chip->mce_bit &= ~CS4231_MCE; | ||
1384 | timeout = cs4231_inb(chip, CS4231P(REGSEL)); | ||
1385 | cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); | ||
1386 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1387 | if (timeout == 0x80) | ||
1388 | snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
1389 | if ((timeout & CS4231_MCE) == 0 || | ||
1390 | !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { | ||
1391 | return; | ||
1392 | } | ||
1393 | snd_cs4231_busy_wait(chip); | ||
1394 | #endif | ||
1395 | } | ||
1396 | |||
1397 | static int snd_cs4231_pm_suspend(snd_card_t *card, pm_message_t state) | ||
1398 | { | ||
1399 | cs4231_t *chip = card->pm_private_data; | ||
1400 | if (chip->suspend) | ||
1401 | chip->suspend(chip); | ||
1402 | return 0; | ||
1403 | } | ||
1404 | |||
1405 | static int snd_cs4231_pm_resume(snd_card_t *card) | ||
1406 | { | ||
1407 | cs4231_t *chip = card->pm_private_data; | ||
1408 | if (chip->resume) | ||
1409 | chip->resume(chip); | ||
1410 | return 0; | ||
1411 | } | ||
1412 | #endif /* CONFIG_PM */ | ||
1413 | |||
1414 | #ifdef LEGACY_SUPPORT | ||
1415 | |||
1416 | static int snd_cs4231_free(cs4231_t *chip) | ||
1417 | { | ||
1418 | if (chip->res_port) { | ||
1419 | release_resource(chip->res_port); | ||
1420 | kfree_nocheck(chip->res_port); | ||
1421 | } | ||
1422 | if (chip->res_cport) { | ||
1423 | release_resource(chip->res_cport); | ||
1424 | kfree_nocheck(chip->res_cport); | ||
1425 | } | ||
1426 | if (chip->irq >= 0) { | ||
1427 | disable_irq(chip->irq); | ||
1428 | if (!(chip->hwshare & CS4231_HWSHARE_IRQ)) | ||
1429 | free_irq(chip->irq, (void *) chip); | ||
1430 | } | ||
1431 | if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) { | ||
1432 | snd_dma_disable(chip->dma1); | ||
1433 | free_dma(chip->dma1); | ||
1434 | } | ||
1435 | if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) { | ||
1436 | snd_dma_disable(chip->dma2); | ||
1437 | free_dma(chip->dma2); | ||
1438 | } | ||
1439 | if (chip->timer) | ||
1440 | snd_device_free(chip->card, chip->timer); | ||
1441 | kfree(chip); | ||
1442 | return 0; | ||
1443 | } | ||
1444 | |||
1445 | static int snd_cs4231_dev_free(snd_device_t *device) | ||
1446 | { | ||
1447 | cs4231_t *chip = device->device_data; | ||
1448 | return snd_cs4231_free(chip); | ||
1449 | } | ||
1450 | |||
1451 | #endif /* LEGACY_SUPPORT */ | ||
1452 | |||
1453 | const char *snd_cs4231_chip_id(cs4231_t *chip) | ||
1454 | { | ||
1455 | switch (chip->hardware) { | ||
1456 | case CS4231_HW_CS4231: return "CS4231"; | ||
1457 | case CS4231_HW_CS4231A: return "CS4231A"; | ||
1458 | case CS4231_HW_CS4232: return "CS4232"; | ||
1459 | case CS4231_HW_CS4232A: return "CS4232A"; | ||
1460 | case CS4231_HW_CS4235: return "CS4235"; | ||
1461 | case CS4231_HW_CS4236: return "CS4236"; | ||
1462 | case CS4231_HW_CS4236B: return "CS4236B"; | ||
1463 | case CS4231_HW_CS4237B: return "CS4237B"; | ||
1464 | case CS4231_HW_CS4238B: return "CS4238B"; | ||
1465 | case CS4231_HW_CS4239: return "CS4239"; | ||
1466 | case CS4231_HW_INTERWAVE: return "AMD InterWave"; | ||
1467 | case CS4231_HW_OPL3SA2: return chip->card->shortname; | ||
1468 | case CS4231_HW_AD1845: return "AD1845"; | ||
1469 | default: return "???"; | ||
1470 | } | ||
1471 | } | ||
1472 | |||
1473 | static int snd_cs4231_new(snd_card_t * card, | ||
1474 | unsigned short hardware, | ||
1475 | unsigned short hwshare, | ||
1476 | cs4231_t ** rchip) | ||
1477 | { | ||
1478 | cs4231_t *chip; | ||
1479 | |||
1480 | *rchip = NULL; | ||
1481 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
1482 | if (chip == NULL) | ||
1483 | return -ENOMEM; | ||
1484 | chip->hardware = hardware; | ||
1485 | chip->hwshare = hwshare; | ||
1486 | |||
1487 | spin_lock_init(&chip->reg_lock); | ||
1488 | init_MUTEX(&chip->mce_mutex); | ||
1489 | init_MUTEX(&chip->open_mutex); | ||
1490 | chip->card = card; | ||
1491 | chip->rate_constraint = snd_cs4231_xrate; | ||
1492 | chip->set_playback_format = snd_cs4231_playback_format; | ||
1493 | chip->set_capture_format = snd_cs4231_capture_format; | ||
1494 | memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); | ||
1495 | |||
1496 | *rchip = chip; | ||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1500 | #ifdef LEGACY_SUPPORT | ||
1501 | |||
1502 | int snd_cs4231_create(snd_card_t * card, | ||
1503 | unsigned long port, | ||
1504 | unsigned long cport, | ||
1505 | int irq, int dma1, int dma2, | ||
1506 | unsigned short hardware, | ||
1507 | unsigned short hwshare, | ||
1508 | cs4231_t ** rchip) | ||
1509 | { | ||
1510 | static snd_device_ops_t ops = { | ||
1511 | .dev_free = snd_cs4231_dev_free, | ||
1512 | }; | ||
1513 | cs4231_t *chip; | ||
1514 | int err; | ||
1515 | |||
1516 | err = snd_cs4231_new(card, hardware, hwshare, &chip); | ||
1517 | if (err < 0) | ||
1518 | return err; | ||
1519 | |||
1520 | chip->irq = -1; | ||
1521 | chip->dma1 = -1; | ||
1522 | chip->dma2 = -1; | ||
1523 | |||
1524 | if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) { | ||
1525 | snd_printk(KERN_ERR "cs4231: can't grab port 0x%lx\n", port); | ||
1526 | snd_cs4231_free(chip); | ||
1527 | return -EBUSY; | ||
1528 | } | ||
1529 | chip->port = port; | ||
1530 | if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) { | ||
1531 | snd_printk(KERN_ERR "cs4231: can't grab control port 0x%lx\n", cport); | ||
1532 | snd_cs4231_free(chip); | ||
1533 | return -ENODEV; | ||
1534 | } | ||
1535 | chip->cport = cport; | ||
1536 | if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) { | ||
1537 | snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq); | ||
1538 | snd_cs4231_free(chip); | ||
1539 | return -EBUSY; | ||
1540 | } | ||
1541 | chip->irq = irq; | ||
1542 | if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) { | ||
1543 | snd_printk(KERN_ERR "cs4231: can't grab DMA1 %d\n", dma1); | ||
1544 | snd_cs4231_free(chip); | ||
1545 | return -EBUSY; | ||
1546 | } | ||
1547 | chip->dma1 = dma1; | ||
1548 | if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) { | ||
1549 | snd_printk(KERN_ERR "cs4231: can't grab DMA2 %d\n", dma2); | ||
1550 | snd_cs4231_free(chip); | ||
1551 | return -EBUSY; | ||
1552 | } | ||
1553 | if (dma1 == dma2 || dma2 < 0) { | ||
1554 | chip->single_dma = 1; | ||
1555 | chip->dma2 = chip->dma1; | ||
1556 | } else | ||
1557 | chip->dma2 = dma2; | ||
1558 | |||
1559 | /* global setup */ | ||
1560 | if (snd_cs4231_probe(chip) < 0) { | ||
1561 | snd_cs4231_free(chip); | ||
1562 | return -ENODEV; | ||
1563 | } | ||
1564 | snd_cs4231_init(chip); | ||
1565 | |||
1566 | if (chip->hardware & CS4231_HW_CS4232_MASK) { | ||
1567 | if (chip->res_cport == NULL) | ||
1568 | snd_printk("CS4232 control port features are not accessible\n"); | ||
1569 | } | ||
1570 | |||
1571 | /* Register device */ | ||
1572 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
1573 | snd_cs4231_free(chip); | ||
1574 | return err; | ||
1575 | } | ||
1576 | |||
1577 | #ifdef CONFIG_PM | ||
1578 | /* Power Management */ | ||
1579 | chip->suspend = snd_cs4231_suspend; | ||
1580 | chip->resume = snd_cs4231_resume; | ||
1581 | snd_card_set_isa_pm_callback(card, snd_cs4231_pm_suspend, snd_cs4231_pm_resume, chip); | ||
1582 | #endif | ||
1583 | |||
1584 | *rchip = chip; | ||
1585 | return 0; | ||
1586 | } | ||
1587 | |||
1588 | #endif /* LEGACY_SUPPORT */ | ||
1589 | |||
1590 | static snd_pcm_ops_t snd_cs4231_playback_ops = { | ||
1591 | .open = snd_cs4231_playback_open, | ||
1592 | .close = snd_cs4231_playback_close, | ||
1593 | .ioctl = snd_pcm_lib_ioctl, | ||
1594 | .hw_params = snd_cs4231_playback_hw_params, | ||
1595 | .hw_free = snd_cs4231_playback_hw_free, | ||
1596 | .prepare = snd_cs4231_playback_prepare, | ||
1597 | .trigger = snd_cs4231_trigger, | ||
1598 | .pointer = snd_cs4231_playback_pointer, | ||
1599 | }; | ||
1600 | |||
1601 | static snd_pcm_ops_t snd_cs4231_capture_ops = { | ||
1602 | .open = snd_cs4231_capture_open, | ||
1603 | .close = snd_cs4231_capture_close, | ||
1604 | .ioctl = snd_pcm_lib_ioctl, | ||
1605 | .hw_params = snd_cs4231_capture_hw_params, | ||
1606 | .hw_free = snd_cs4231_capture_hw_free, | ||
1607 | .prepare = snd_cs4231_capture_prepare, | ||
1608 | .trigger = snd_cs4231_trigger, | ||
1609 | .pointer = snd_cs4231_capture_pointer, | ||
1610 | }; | ||
1611 | |||
1612 | static void snd_cs4231_pcm_free(snd_pcm_t *pcm) | ||
1613 | { | ||
1614 | cs4231_t *chip = pcm->private_data; | ||
1615 | chip->pcm = NULL; | ||
1616 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
1617 | } | ||
1618 | |||
1619 | int snd_cs4231_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) | ||
1620 | { | ||
1621 | snd_pcm_t *pcm; | ||
1622 | int err; | ||
1623 | |||
1624 | if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0) | ||
1625 | return err; | ||
1626 | |||
1627 | spin_lock_init(&chip->reg_lock); | ||
1628 | init_MUTEX(&chip->mce_mutex); | ||
1629 | init_MUTEX(&chip->open_mutex); | ||
1630 | |||
1631 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops); | ||
1632 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops); | ||
1633 | |||
1634 | /* global setup */ | ||
1635 | pcm->private_data = chip; | ||
1636 | pcm->private_free = snd_cs4231_pcm_free; | ||
1637 | pcm->info_flags = 0; | ||
1638 | if (chip->single_dma) | ||
1639 | pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; | ||
1640 | if (chip->hardware != CS4231_HW_INTERWAVE) | ||
1641 | pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
1642 | strcpy(pcm->name, snd_cs4231_chip_id(chip)); | ||
1643 | |||
1644 | #ifdef LEGACY_SUPPORT | ||
1645 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
1646 | snd_dma_isa_data(), | ||
1647 | 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); | ||
1648 | #else | ||
1649 | # ifdef EBUS_SUPPORT | ||
1650 | if (chip->ebus_flag) { | ||
1651 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
1652 | chip->dev_u.pdev, | ||
1653 | 64*1024, 128*1024); | ||
1654 | } else { | ||
1655 | # endif | ||
1656 | # ifdef SBUS_SUPPORT | ||
1657 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, | ||
1658 | chip->dev_u.sdev, | ||
1659 | 64*1024, 128*1024); | ||
1660 | # endif | ||
1661 | # ifdef EBUS_SUPPORT | ||
1662 | } | ||
1663 | # endif | ||
1664 | #endif | ||
1665 | |||
1666 | chip->pcm = pcm; | ||
1667 | if (rpcm) | ||
1668 | *rpcm = pcm; | ||
1669 | return 0; | ||
1670 | } | ||
1671 | |||
1672 | static void snd_cs4231_timer_free(snd_timer_t *timer) | ||
1673 | { | ||
1674 | cs4231_t *chip = timer->private_data; | ||
1675 | chip->timer = NULL; | ||
1676 | } | ||
1677 | |||
1678 | int snd_cs4231_timer(cs4231_t *chip, int device, snd_timer_t **rtimer) | ||
1679 | { | ||
1680 | snd_timer_t *timer; | ||
1681 | snd_timer_id_t tid; | ||
1682 | int err; | ||
1683 | |||
1684 | /* Timer initialization */ | ||
1685 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
1686 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
1687 | tid.card = chip->card->number; | ||
1688 | tid.device = device; | ||
1689 | tid.subdevice = 0; | ||
1690 | if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) | ||
1691 | return err; | ||
1692 | strcpy(timer->name, snd_cs4231_chip_id(chip)); | ||
1693 | timer->private_data = chip; | ||
1694 | timer->private_free = snd_cs4231_timer_free; | ||
1695 | timer->hw = snd_cs4231_timer_table; | ||
1696 | chip->timer = timer; | ||
1697 | if (rtimer) | ||
1698 | *rtimer = timer; | ||
1699 | return 0; | ||
1700 | } | ||
1701 | |||
1702 | /* | ||
1703 | * MIXER part | ||
1704 | */ | ||
1705 | |||
1706 | static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1707 | { | ||
1708 | static char *texts[4] = { | ||
1709 | "Line", "Aux", "Mic", "Mix" | ||
1710 | }; | ||
1711 | static char *opl3sa_texts[4] = { | ||
1712 | "Line", "CD", "Mic", "Mix" | ||
1713 | }; | ||
1714 | static char *gusmax_texts[4] = { | ||
1715 | "Line", "Synth", "Mic", "Mix" | ||
1716 | }; | ||
1717 | char **ptexts = texts; | ||
1718 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1719 | |||
1720 | snd_assert(chip->card != NULL, return -EINVAL); | ||
1721 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
1722 | uinfo->count = 2; | ||
1723 | uinfo->value.enumerated.items = 4; | ||
1724 | if (uinfo->value.enumerated.item > 3) | ||
1725 | uinfo->value.enumerated.item = 3; | ||
1726 | if (!strcmp(chip->card->driver, "GUS MAX")) | ||
1727 | ptexts = gusmax_texts; | ||
1728 | switch (chip->hardware) { | ||
1729 | case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break; | ||
1730 | case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break; | ||
1731 | } | ||
1732 | strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); | ||
1733 | return 0; | ||
1734 | } | ||
1735 | |||
1736 | static int snd_cs4231_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1737 | { | ||
1738 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1739 | unsigned long flags; | ||
1740 | |||
1741 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1742 | ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; | ||
1743 | ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; | ||
1744 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1745 | return 0; | ||
1746 | } | ||
1747 | |||
1748 | static int snd_cs4231_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1749 | { | ||
1750 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1751 | unsigned long flags; | ||
1752 | unsigned short left, right; | ||
1753 | int change; | ||
1754 | |||
1755 | if (ucontrol->value.enumerated.item[0] > 3 || | ||
1756 | ucontrol->value.enumerated.item[1] > 3) | ||
1757 | return -EINVAL; | ||
1758 | left = ucontrol->value.enumerated.item[0] << 6; | ||
1759 | right = ucontrol->value.enumerated.item[1] << 6; | ||
1760 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1761 | left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; | ||
1762 | right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; | ||
1763 | change = left != chip->image[CS4231_LEFT_INPUT] || | ||
1764 | right != chip->image[CS4231_RIGHT_INPUT]; | ||
1765 | snd_cs4231_out(chip, CS4231_LEFT_INPUT, left); | ||
1766 | snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right); | ||
1767 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1768 | return change; | ||
1769 | } | ||
1770 | |||
1771 | int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1772 | { | ||
1773 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1774 | |||
1775 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1776 | uinfo->count = 1; | ||
1777 | uinfo->value.integer.min = 0; | ||
1778 | uinfo->value.integer.max = mask; | ||
1779 | return 0; | ||
1780 | } | ||
1781 | |||
1782 | int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1783 | { | ||
1784 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1785 | unsigned long flags; | ||
1786 | int reg = kcontrol->private_value & 0xff; | ||
1787 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1788 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1789 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1790 | |||
1791 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1792 | ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; | ||
1793 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1794 | if (invert) | ||
1795 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1796 | return 0; | ||
1797 | } | ||
1798 | |||
1799 | int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1800 | { | ||
1801 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1802 | unsigned long flags; | ||
1803 | int reg = kcontrol->private_value & 0xff; | ||
1804 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1805 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1806 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1807 | int change; | ||
1808 | unsigned short val; | ||
1809 | |||
1810 | val = (ucontrol->value.integer.value[0] & mask); | ||
1811 | if (invert) | ||
1812 | val = mask - val; | ||
1813 | val <<= shift; | ||
1814 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1815 | val = (chip->image[reg] & ~(mask << shift)) | val; | ||
1816 | change = val != chip->image[reg]; | ||
1817 | snd_cs4231_out(chip, reg, val); | ||
1818 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1819 | return change; | ||
1820 | } | ||
1821 | |||
1822 | int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1823 | { | ||
1824 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1825 | |||
1826 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1827 | uinfo->count = 2; | ||
1828 | uinfo->value.integer.min = 0; | ||
1829 | uinfo->value.integer.max = mask; | ||
1830 | return 0; | ||
1831 | } | ||
1832 | |||
1833 | int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1834 | { | ||
1835 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1836 | unsigned long flags; | ||
1837 | int left_reg = kcontrol->private_value & 0xff; | ||
1838 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1839 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1840 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1841 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1842 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1843 | |||
1844 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1845 | ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; | ||
1846 | ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; | ||
1847 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1848 | if (invert) { | ||
1849 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1850 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
1851 | } | ||
1852 | return 0; | ||
1853 | } | ||
1854 | |||
1855 | int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1856 | { | ||
1857 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
1858 | unsigned long flags; | ||
1859 | int left_reg = kcontrol->private_value & 0xff; | ||
1860 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1861 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1862 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1863 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1864 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1865 | int change; | ||
1866 | unsigned short val1, val2; | ||
1867 | |||
1868 | val1 = ucontrol->value.integer.value[0] & mask; | ||
1869 | val2 = ucontrol->value.integer.value[1] & mask; | ||
1870 | if (invert) { | ||
1871 | val1 = mask - val1; | ||
1872 | val2 = mask - val2; | ||
1873 | } | ||
1874 | val1 <<= shift_left; | ||
1875 | val2 <<= shift_right; | ||
1876 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1877 | val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; | ||
1878 | val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; | ||
1879 | change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; | ||
1880 | snd_cs4231_out(chip, left_reg, val1); | ||
1881 | snd_cs4231_out(chip, right_reg, val2); | ||
1882 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1883 | return change; | ||
1884 | } | ||
1885 | |||
1886 | static snd_kcontrol_new_t snd_cs4231_controls[] = { | ||
1887 | CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
1888 | CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), | ||
1889 | CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), | ||
1890 | CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), | ||
1891 | CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), | ||
1892 | CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), | ||
1893 | CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), | ||
1894 | CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), | ||
1895 | CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1), | ||
1896 | CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), | ||
1897 | CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1), | ||
1898 | CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), | ||
1899 | CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), | ||
1900 | { | ||
1901 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1902 | .name = "Capture Source", | ||
1903 | .info = snd_cs4231_info_mux, | ||
1904 | .get = snd_cs4231_get_mux, | ||
1905 | .put = snd_cs4231_put_mux, | ||
1906 | }, | ||
1907 | CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), | ||
1908 | CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), | ||
1909 | CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1) | ||
1910 | }; | ||
1911 | |||
1912 | int snd_cs4231_mixer(cs4231_t *chip) | ||
1913 | { | ||
1914 | snd_card_t *card; | ||
1915 | unsigned int idx; | ||
1916 | int err; | ||
1917 | |||
1918 | snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); | ||
1919 | |||
1920 | card = chip->card; | ||
1921 | |||
1922 | strcpy(card->mixername, chip->pcm->name); | ||
1923 | |||
1924 | for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { | ||
1925 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) | ||
1926 | return err; | ||
1927 | } | ||
1928 | return 0; | ||
1929 | } | ||
1930 | |||
1931 | EXPORT_SYMBOL(snd_cs4231_out); | ||
1932 | EXPORT_SYMBOL(snd_cs4231_in); | ||
1933 | EXPORT_SYMBOL(snd_cs4236_ext_out); | ||
1934 | EXPORT_SYMBOL(snd_cs4236_ext_in); | ||
1935 | EXPORT_SYMBOL(snd_cs4231_mce_up); | ||
1936 | EXPORT_SYMBOL(snd_cs4231_mce_down); | ||
1937 | EXPORT_SYMBOL(snd_cs4231_interrupt); | ||
1938 | EXPORT_SYMBOL(snd_cs4231_chip_id); | ||
1939 | EXPORT_SYMBOL(snd_cs4231_create); | ||
1940 | EXPORT_SYMBOL(snd_cs4231_pcm); | ||
1941 | EXPORT_SYMBOL(snd_cs4231_mixer); | ||
1942 | EXPORT_SYMBOL(snd_cs4231_timer); | ||
1943 | EXPORT_SYMBOL(snd_cs4231_info_single); | ||
1944 | EXPORT_SYMBOL(snd_cs4231_get_single); | ||
1945 | EXPORT_SYMBOL(snd_cs4231_put_single); | ||
1946 | EXPORT_SYMBOL(snd_cs4231_info_double); | ||
1947 | EXPORT_SYMBOL(snd_cs4231_get_double); | ||
1948 | EXPORT_SYMBOL(snd_cs4231_put_double); | ||
1949 | |||
1950 | /* | ||
1951 | * INIT part | ||
1952 | */ | ||
1953 | |||
1954 | static int __init alsa_cs4231_init(void) | ||
1955 | { | ||
1956 | return 0; | ||
1957 | } | ||
1958 | |||
1959 | static void __exit alsa_cs4231_exit(void) | ||
1960 | { | ||
1961 | } | ||
1962 | |||
1963 | module_init(alsa_cs4231_init) | ||
1964 | module_exit(alsa_cs4231_exit) | ||
diff --git a/sound/isa/cs423x/cs4232.c b/sound/isa/cs423x/cs4232.c new file mode 100644 index 000000000000..9fad2e6c0c2c --- /dev/null +++ b/sound/isa/cs423x/cs4232.c | |||
@@ -0,0 +1,2 @@ | |||
1 | #define CS4232 | ||
2 | #include "cs4236.c" | ||
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c new file mode 100644 index 000000000000..e745a54e00a1 --- /dev/null +++ b/sound/isa/cs423x/cs4236.c | |||
@@ -0,0 +1,608 @@ | |||
1 | /* | ||
2 | * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/pnp.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/cs4231.h> | ||
29 | #include <sound/mpu401.h> | ||
30 | #include <sound/opl3.h> | ||
31 | #include <sound/initval.h> | ||
32 | |||
33 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | #ifdef CS4232 | ||
36 | MODULE_DESCRIPTION("Cirrus Logic CS4232"); | ||
37 | MODULE_SUPPORTED_DEVICE("{{Turtle Beach,TBS-2000}," | ||
38 | "{Turtle Beach,Tropez Plus}," | ||
39 | "{SIC CrystalWave 32}," | ||
40 | "{Hewlett Packard,Omnibook 5500}," | ||
41 | "{TerraTec,Maestro 32/96}," | ||
42 | "{Philips,PCA70PS}}"); | ||
43 | #else | ||
44 | MODULE_DESCRIPTION("Cirrus Logic CS4235-9"); | ||
45 | MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4235}," | ||
46 | "{Crystal Semiconductors,CS4236}," | ||
47 | "{Crystal Semiconductors,CS4237}," | ||
48 | "{Crystal Semiconductors,CS4238}," | ||
49 | "{Crystal Semiconductors,CS4239}," | ||
50 | "{Acer,AW37}," | ||
51 | "{Acer,AW35/Pro}," | ||
52 | "{Crystal,3D}," | ||
53 | "{Crystal Computer,TidalWave128}," | ||
54 | "{Dell,Optiplex GX1}," | ||
55 | "{Dell,Workstation 400 sound}," | ||
56 | "{EliteGroup,P5TX-LA sound}," | ||
57 | "{Gallant,SC-70P}," | ||
58 | "{Gateway,E1000 Onboard CS4236B}," | ||
59 | "{Genius,Sound Maker 3DJ}," | ||
60 | "{Hewlett Packard,HP6330 sound}," | ||
61 | "{IBM,PC 300PL sound}," | ||
62 | "{IBM,Aptiva 2137 E24}," | ||
63 | "{IBM,IntelliStation M Pro}," | ||
64 | "{Intel,Marlin Spike Mobo CS4235}," | ||
65 | "{Intel PR440FX Onboard}," | ||
66 | "{Guillemot,MaxiSound 16 PnP}," | ||
67 | "{NewClear,3D}," | ||
68 | "{TerraTec,AudioSystem EWS64L/XL}," | ||
69 | "{Typhoon Soundsystem,CS4236B}," | ||
70 | "{Turtle Beach,Malibu}," | ||
71 | "{Unknown,Digital PC 5000 Onboard}}"); | ||
72 | #endif | ||
73 | |||
74 | #ifdef CS4232 | ||
75 | #define IDENT "CS4232" | ||
76 | #else | ||
77 | #define IDENT "CS4236+" | ||
78 | #endif | ||
79 | |||
80 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
81 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
82 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
83 | #ifdef CONFIG_PNP | ||
84 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
85 | #endif | ||
86 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
87 | static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
88 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */ | ||
89 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
90 | static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
91 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ | ||
92 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ | ||
93 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
94 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
95 | |||
96 | module_param_array(index, int, NULL, 0444); | ||
97 | MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard."); | ||
98 | module_param_array(id, charp, NULL, 0444); | ||
99 | MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard."); | ||
100 | module_param_array(enable, bool, NULL, 0444); | ||
101 | MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard."); | ||
102 | #ifdef CONFIG_PNP | ||
103 | module_param_array(isapnp, bool, NULL, 0444); | ||
104 | MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); | ||
105 | #endif | ||
106 | module_param_array(port, long, NULL, 0444); | ||
107 | MODULE_PARM_DESC(port, "Port # for " IDENT " driver."); | ||
108 | module_param_array(cport, long, NULL, 0444); | ||
109 | MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver."); | ||
110 | module_param_array(mpu_port, long, NULL, 0444); | ||
111 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver."); | ||
112 | module_param_array(fm_port, long, NULL, 0444); | ||
113 | MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver."); | ||
114 | module_param_array(sb_port, long, NULL, 0444); | ||
115 | MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional)."); | ||
116 | module_param_array(irq, int, NULL, 0444); | ||
117 | MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver."); | ||
118 | module_param_array(mpu_irq, int, NULL, 0444); | ||
119 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); | ||
120 | module_param_array(dma1, int, NULL, 0444); | ||
121 | MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver."); | ||
122 | module_param_array(dma2, int, NULL, 0444); | ||
123 | MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver."); | ||
124 | |||
125 | struct snd_card_cs4236 { | ||
126 | struct resource *res_sb_port; | ||
127 | #ifdef CONFIG_PNP | ||
128 | struct pnp_dev *wss; | ||
129 | struct pnp_dev *ctrl; | ||
130 | struct pnp_dev *mpu; | ||
131 | #endif | ||
132 | }; | ||
133 | |||
134 | static snd_card_t *snd_cs4236_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
135 | |||
136 | #ifdef CONFIG_PNP | ||
137 | |||
138 | #define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ | ||
139 | { \ | ||
140 | ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ | ||
141 | .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ | ||
142 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ | ||
143 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \ | ||
144 | } | ||
145 | #define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ | ||
146 | { \ | ||
147 | ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ | ||
148 | .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ | ||
149 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ | ||
150 | ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \ | ||
151 | } | ||
152 | #define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \ | ||
153 | { \ | ||
154 | ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ | ||
155 | .devs = { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ | ||
156 | ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \ | ||
157 | } | ||
158 | |||
159 | |||
160 | #ifdef CS4232 | ||
161 | static struct pnp_card_device_id snd_cs423x_pnpids[] = { | ||
162 | /* Philips PCA70PS */ | ||
163 | { .id = "CSC0d32", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } }, | ||
164 | /* TerraTec Maestro 32/96 (CS4232) */ | ||
165 | { .id = "CSC1a32", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
166 | /* HP Omnibook 5500 onboard */ | ||
167 | { .id = "CSC4232", .devs = { { "CSC0000" }, { "CSC0002" }, { "CSC0003" } } }, | ||
168 | /* Unnamed CS4236 card (Made in Taiwan) */ | ||
169 | { .id = "CSC4236", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
170 | /* Turtle Beach TBS-2000 (CS4232) */ | ||
171 | { .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSCb006" } } }, | ||
172 | /* Turtle Beach Tropez Plus (CS4232) */ | ||
173 | { .id = "CSC7632", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } }, | ||
174 | /* SIC CrystalWave 32 (CS4232) */ | ||
175 | { .id = "CSCf032", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
176 | /* --- */ | ||
177 | { .id = "" } /* end */ | ||
178 | }; | ||
179 | #else /* CS4236 */ | ||
180 | static struct pnp_card_device_id snd_cs423x_pnpids[] = { | ||
181 | /* Intel Marlin Spike Motherboard - CS4235 */ | ||
182 | { .id = "CSC0225", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
183 | /* Intel Marlin Spike Motherboard (#2) - CS4235 */ | ||
184 | { .id = "CSC0225", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
185 | /* Unknown Intel mainboard - CS4235 */ | ||
186 | { .id = "CSC0225", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
187 | /* Genius Sound Maker 3DJ - CS4237B */ | ||
188 | { .id = "CSC0437", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
189 | /* Digital PC 5000 Onboard - CS4236B */ | ||
190 | { .id = "CSC0735", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
191 | /* some uknown CS4236B */ | ||
192 | { .id = "CSC0b35", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
193 | /* Intel PR440FX Onboard sound */ | ||
194 | { .id = "CSC0b36", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
195 | /* CS4235 on mainboard without MPU */ | ||
196 | { .id = "CSC1425", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
197 | /* Gateway E1000 Onboard CS4236B */ | ||
198 | { .id = "CSC1335", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
199 | /* HP 6330 Onboard sound */ | ||
200 | { .id = "CSC1525", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
201 | /* Crystal Computer TidalWave128 */ | ||
202 | { .id = "CSC1e37", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
203 | /* ACER AW37 - CS4235 */ | ||
204 | { .id = "CSC4236", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
205 | /* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */ | ||
206 | { .id = "CSC4237", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
207 | /* Crystal 3D - CS4237B */ | ||
208 | { .id = "CSC4336", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
209 | /* Typhoon Soundsystem PnP - CS4236B */ | ||
210 | { .id = "CSC4536", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
211 | /* Crystal CX4235-XQ3 EP - CS4235 */ | ||
212 | { .id = "CSC4625", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
213 | /* Crystal Semiconductors CS4237B */ | ||
214 | { .id = "CSC4637", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
215 | /* NewClear 3D - CX4237B-XQ3 */ | ||
216 | { .id = "CSC4837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
217 | /* Dell Optiplex GX1 - CS4236B */ | ||
218 | { .id = "CSC6835", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
219 | /* Dell P410 motherboard - CS4236B */ | ||
220 | { .id = "CSC6835", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
221 | /* Dell Workstation 400 Onboard - CS4236B */ | ||
222 | { .id = "CSC6836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
223 | /* Turtle Beach Malibu - CS4237B */ | ||
224 | { .id = "CSC7537", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
225 | /* CS4235 - onboard */ | ||
226 | { .id = "CSC8025", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC0103" } } }, | ||
227 | /* IBM Aptiva 2137 E24 Onboard - CS4237B */ | ||
228 | { .id = "CSC8037", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
229 | /* IBM IntelliStation M Pro motherboard */ | ||
230 | { .id = "CSCc835", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
231 | /* Guillemot MaxiSound 16 PnP - CS4236B */ | ||
232 | { .id = "CSC9836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
233 | /* Gallant SC-70P */ | ||
234 | { .id = "CSC9837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
235 | /* TerraTec AudioSystem EWS64XL - CS4236B */ | ||
236 | { .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" }, { "CSCa803" } } }, | ||
237 | /* TerraTec AudioSystem EWS64XL - CS4236B */ | ||
238 | { .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" } } }, | ||
239 | /* ACER AW37/Pro - CS4235 */ | ||
240 | { .id = "CSCd925", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
241 | /* ACER AW35/Pro - CS4237B */ | ||
242 | { .id = "CSCd937", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
243 | /* CS4235 without MPU401 */ | ||
244 | { .id = "CSCe825", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
245 | /* Unknown SiS530 - CS4235 */ | ||
246 | { .id = "CSC4825", .devs = { { "CSC0100" }, { "CSC0110" } } }, | ||
247 | /* IBM IntelliStation M Pro 6898 11U - CS4236B */ | ||
248 | { .id = "CSCe835", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
249 | /* IBM PC 300PL Onboard - CS4236B */ | ||
250 | { .id = "CSCe836", .devs = { { "CSC0000" }, { "CSC0010" } } }, | ||
251 | /* Some noname CS4236 based card */ | ||
252 | { .id = "CSCe936", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
253 | /* CS4236B */ | ||
254 | { .id = "CSCf235", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
255 | /* CS4236B */ | ||
256 | { .id = "CSCf238", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, | ||
257 | /* --- */ | ||
258 | { .id = "" } /* end */ | ||
259 | }; | ||
260 | #endif | ||
261 | |||
262 | MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids); | ||
263 | |||
264 | static int __devinit snd_card_cs4236_pnp(int dev, struct snd_card_cs4236 *acard, | ||
265 | struct pnp_card_link *card, | ||
266 | const struct pnp_card_device_id *id) | ||
267 | { | ||
268 | struct pnp_dev *pdev; | ||
269 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
270 | int err; | ||
271 | |||
272 | if (!cfg) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
276 | if (acard->wss == NULL) { | ||
277 | kfree(cfg); | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
281 | if (acard->ctrl == NULL) { | ||
282 | kfree(cfg); | ||
283 | return -EBUSY; | ||
284 | } | ||
285 | if (id->devs[2].id[0]) { | ||
286 | acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); | ||
287 | if (acard->mpu == NULL) { | ||
288 | kfree(cfg); | ||
289 | return -EBUSY; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* WSS initialization */ | ||
294 | pdev = acard->wss; | ||
295 | pnp_init_resource_table(cfg); | ||
296 | if (port[dev] != SNDRV_AUTO_PORT) | ||
297 | pnp_resource_change(&cfg->port_resource[0], port[dev], 4); | ||
298 | if (fm_port[dev] != SNDRV_AUTO_PORT && fm_port[dev] > 0) | ||
299 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
300 | if (sb_port[dev] != SNDRV_AUTO_PORT) | ||
301 | pnp_resource_change(&cfg->port_resource[2], sb_port[dev], 16); | ||
302 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
303 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
304 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
305 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
306 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
307 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev] < 0 ? 4 : dma2[dev], 1); | ||
308 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
309 | if (err < 0) | ||
310 | snd_printk(KERN_ERR IDENT " WSS PnP manual resources are invalid, using auto config\n"); | ||
311 | err = pnp_activate_dev(pdev); | ||
312 | if (err < 0) { | ||
313 | kfree(cfg); | ||
314 | printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n"); | ||
315 | return -EBUSY; | ||
316 | } | ||
317 | port[dev] = pnp_port_start(pdev, 0); | ||
318 | if (fm_port[dev] > 0) | ||
319 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
320 | sb_port[dev] = pnp_port_start(pdev, 2); | ||
321 | irq[dev] = pnp_irq(pdev, 0); | ||
322 | dma1[dev] = pnp_dma(pdev, 0); | ||
323 | dma2[dev] = pnp_dma(pdev, 1) == 4 ? -1 : (int)pnp_dma(pdev, 1); | ||
324 | snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n", | ||
325 | port[dev], fm_port[dev], sb_port[dev]); | ||
326 | snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n", | ||
327 | irq[dev], dma1[dev], dma2[dev]); | ||
328 | /* CTRL initialization */ | ||
329 | if (acard->ctrl && cport[dev] > 0) { | ||
330 | pdev = acard->ctrl; | ||
331 | pnp_init_resource_table(cfg); | ||
332 | if (cport[dev] != SNDRV_AUTO_PORT) | ||
333 | pnp_resource_change(&cfg->port_resource[0], cport[dev], 8); | ||
334 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
335 | if (err < 0) | ||
336 | snd_printk(KERN_ERR IDENT " CTRL PnP manual resources are invalid, using auto config\n"); | ||
337 | err = pnp_activate_dev(pdev); | ||
338 | if (err < 0) { | ||
339 | kfree(cfg); | ||
340 | printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n"); | ||
341 | return -EBUSY; | ||
342 | } | ||
343 | cport[dev] = pnp_port_start(pdev, 0); | ||
344 | snd_printdd("isapnp CTRL: control port=0x%lx\n", cport[dev]); | ||
345 | } | ||
346 | /* MPU initialization */ | ||
347 | if (acard->mpu && mpu_port[dev] > 0) { | ||
348 | pdev = acard->mpu; | ||
349 | pnp_init_resource_table(cfg); | ||
350 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
351 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
352 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0 && | ||
353 | pnp_irq_valid(pdev, 0)) | ||
354 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
355 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
356 | if (err < 0) | ||
357 | snd_printk(KERN_ERR IDENT " MPU401 PnP manual resources are invalid, using auto config\n"); | ||
358 | err = pnp_activate_dev(pdev); | ||
359 | if (err < 0) { | ||
360 | printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n"); | ||
361 | mpu_port[dev] = SNDRV_AUTO_PORT; | ||
362 | mpu_irq[dev] = SNDRV_AUTO_IRQ; | ||
363 | } else { | ||
364 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
365 | if (mpu_irq[dev] >= 0 && | ||
366 | pnp_irq_valid(pdev, 0) && pnp_irq(pdev, 0) >= 0) { | ||
367 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
368 | } else { | ||
369 | mpu_irq[dev] = -1; /* disable interrupt */ | ||
370 | } | ||
371 | } | ||
372 | snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]); | ||
373 | } | ||
374 | kfree(cfg); | ||
375 | return 0; | ||
376 | } | ||
377 | #endif /* CONFIG_PNP */ | ||
378 | |||
379 | static void snd_card_cs4236_free(snd_card_t *card) | ||
380 | { | ||
381 | struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data; | ||
382 | |||
383 | if (acard) { | ||
384 | if (acard->res_sb_port) { | ||
385 | release_resource(acard->res_sb_port); | ||
386 | kfree_nocheck(acard->res_sb_port); | ||
387 | } | ||
388 | } | ||
389 | } | ||
390 | |||
391 | static int __devinit snd_card_cs423x_probe(int dev, struct pnp_card_link *pcard, | ||
392 | const struct pnp_card_device_id *pid) | ||
393 | { | ||
394 | snd_card_t *card; | ||
395 | struct snd_card_cs4236 *acard; | ||
396 | snd_pcm_t *pcm = NULL; | ||
397 | cs4231_t *chip; | ||
398 | opl3_t *opl3; | ||
399 | int err; | ||
400 | |||
401 | #ifdef CONFIG_PNP | ||
402 | if (!isapnp[dev]) { | ||
403 | #endif | ||
404 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
405 | snd_printk("specify port\n"); | ||
406 | return -EINVAL; | ||
407 | } | ||
408 | if (cport[dev] == SNDRV_AUTO_PORT) { | ||
409 | snd_printk("specify cport\n"); | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | #ifdef CONFIG_PNP | ||
413 | } | ||
414 | #endif | ||
415 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
416 | sizeof(struct snd_card_cs4236)); | ||
417 | if (card == NULL) | ||
418 | return -ENOMEM; | ||
419 | acard = (struct snd_card_cs4236 *)card->private_data; | ||
420 | card->private_free = snd_card_cs4236_free; | ||
421 | #ifdef CONFIG_PNP | ||
422 | if (isapnp[dev]) { | ||
423 | if ((err = snd_card_cs4236_pnp(dev, acard, pcard, pid))<0) { | ||
424 | printk(KERN_ERR "isapnp detection failed and probing for " IDENT " is not supported\n"); | ||
425 | snd_card_free(card); | ||
426 | return -ENXIO; | ||
427 | } | ||
428 | snd_card_set_dev(card, &pcard->card->dev); | ||
429 | } | ||
430 | #endif | ||
431 | if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) | ||
432 | if ((acard->res_sb_port = request_region(sb_port[dev], 16, IDENT " SB")) == NULL) { | ||
433 | printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]); | ||
434 | snd_card_free(card); | ||
435 | return -ENOMEM; | ||
436 | } | ||
437 | |||
438 | #ifdef CS4232 | ||
439 | if ((err = snd_cs4231_create(card, | ||
440 | port[dev], | ||
441 | cport[dev], | ||
442 | irq[dev], | ||
443 | dma1[dev], | ||
444 | dma2[dev], | ||
445 | CS4231_HW_DETECT, | ||
446 | 0, | ||
447 | &chip)) < 0) { | ||
448 | snd_card_free(card); | ||
449 | return err; | ||
450 | } | ||
451 | if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { | ||
452 | snd_card_free(card); | ||
453 | return err; | ||
454 | } | ||
455 | if ((err = snd_cs4231_mixer(chip)) < 0) { | ||
456 | snd_card_free(card); | ||
457 | return err; | ||
458 | } | ||
459 | |||
460 | #else /* CS4236 */ | ||
461 | if ((err = snd_cs4236_create(card, | ||
462 | port[dev], | ||
463 | cport[dev], | ||
464 | irq[dev], | ||
465 | dma1[dev], | ||
466 | dma2[dev], | ||
467 | CS4231_HW_DETECT, | ||
468 | 0, | ||
469 | &chip)) < 0) { | ||
470 | snd_card_free(card); | ||
471 | return err; | ||
472 | } | ||
473 | if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) { | ||
474 | snd_card_free(card); | ||
475 | return err; | ||
476 | } | ||
477 | if ((err = snd_cs4236_mixer(chip)) < 0) { | ||
478 | snd_card_free(card); | ||
479 | return err; | ||
480 | } | ||
481 | #endif | ||
482 | strcpy(card->driver, pcm->name); | ||
483 | strcpy(card->shortname, pcm->name); | ||
484 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", | ||
485 | pcm->name, | ||
486 | chip->port, | ||
487 | irq[dev], | ||
488 | dma1[dev]); | ||
489 | if (dma2[dev] >= 0) | ||
490 | sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); | ||
491 | |||
492 | if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { | ||
493 | snd_card_free(card); | ||
494 | return err; | ||
495 | } | ||
496 | |||
497 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
498 | if (snd_opl3_create(card, | ||
499 | fm_port[dev], fm_port[dev] + 2, | ||
500 | OPL3_HW_OPL3_CS, 0, &opl3) < 0) { | ||
501 | printk(KERN_ERR IDENT ": OPL3 not detected\n"); | ||
502 | } else { | ||
503 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
504 | snd_card_free(card); | ||
505 | return err; | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | |||
510 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
511 | if (mpu_irq[dev] == SNDRV_AUTO_IRQ) | ||
512 | mpu_irq[dev] = -1; | ||
513 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, | ||
514 | mpu_port[dev], 0, | ||
515 | mpu_irq[dev], | ||
516 | mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) | ||
517 | printk(KERN_ERR IDENT ": MPU401 not detected\n"); | ||
518 | } | ||
519 | if ((err = snd_card_register(card)) < 0) { | ||
520 | snd_card_free(card); | ||
521 | return err; | ||
522 | } | ||
523 | if (pcard) | ||
524 | pnp_set_card_drvdata(pcard, card); | ||
525 | else | ||
526 | snd_cs4236_legacy[dev] = card; | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | #ifdef CONFIG_PNP | ||
531 | static int __devinit snd_cs423x_pnp_detect(struct pnp_card_link *card, | ||
532 | const struct pnp_card_device_id *id) | ||
533 | { | ||
534 | static int dev; | ||
535 | int res; | ||
536 | |||
537 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
538 | if (!enable[dev] || !isapnp[dev]) | ||
539 | continue; | ||
540 | res = snd_card_cs423x_probe(dev, card, id); | ||
541 | if (res < 0) | ||
542 | return res; | ||
543 | dev++; | ||
544 | return 0; | ||
545 | } | ||
546 | return -ENODEV; | ||
547 | } | ||
548 | |||
549 | static void __devexit snd_cs423x_pnp_remove(struct pnp_card_link * pcard) | ||
550 | { | ||
551 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
552 | |||
553 | snd_card_disconnect(card); | ||
554 | snd_card_free_in_thread(card); | ||
555 | } | ||
556 | |||
557 | static struct pnp_card_driver cs423x_pnpc_driver = { | ||
558 | .flags = PNP_DRIVER_RES_DISABLE, | ||
559 | .name = "cs423x", | ||
560 | .id_table = snd_cs423x_pnpids, | ||
561 | .probe = snd_cs423x_pnp_detect, | ||
562 | .remove = __devexit_p(snd_cs423x_pnp_remove), | ||
563 | }; | ||
564 | #endif /* CONFIG_PNP */ | ||
565 | |||
566 | static int __init alsa_card_cs423x_init(void) | ||
567 | { | ||
568 | int dev, cards = 0; | ||
569 | |||
570 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
571 | if (!enable[dev]) | ||
572 | continue; | ||
573 | #ifdef CONFIG_PNP | ||
574 | if (isapnp[dev]) | ||
575 | continue; | ||
576 | #endif | ||
577 | if (snd_card_cs423x_probe(dev, NULL, NULL) >= 0) | ||
578 | cards++; | ||
579 | } | ||
580 | #ifdef CONFIG_PNP | ||
581 | cards += pnp_register_card_driver(&cs423x_pnpc_driver); | ||
582 | #endif | ||
583 | if (!cards) { | ||
584 | #ifdef CONFIG_PNP | ||
585 | pnp_unregister_card_driver(&cs423x_pnpc_driver); | ||
586 | #endif | ||
587 | #ifdef MODULE | ||
588 | printk(KERN_ERR IDENT " soundcard not found or device busy\n"); | ||
589 | #endif | ||
590 | return -ENODEV; | ||
591 | } | ||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static void __exit alsa_card_cs423x_exit(void) | ||
596 | { | ||
597 | int idx; | ||
598 | |||
599 | #ifdef CONFIG_PNP | ||
600 | /* PnP cards first */ | ||
601 | pnp_unregister_card_driver(&cs423x_pnpc_driver); | ||
602 | #endif | ||
603 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
604 | snd_card_free(snd_cs4236_legacy[idx]); | ||
605 | } | ||
606 | |||
607 | module_init(alsa_card_cs423x_init) | ||
608 | module_exit(alsa_card_cs423x_exit) | ||
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c new file mode 100644 index 000000000000..2128d4bdef41 --- /dev/null +++ b/sound/isa/cs423x/cs4236_lib.c | |||
@@ -0,0 +1,970 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of CS4235/4236B/4237B/4238B/4239 chips | ||
4 | * | ||
5 | * Note: | ||
6 | * ----- | ||
7 | * | ||
8 | * Bugs: | ||
9 | * ----- | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | /* | ||
28 | * Indirect control registers (CS4236B+) | ||
29 | * | ||
30 | * C0 | ||
31 | * D8: WSS reset (all chips) | ||
32 | * | ||
33 | * C1 (all chips except CS4236) | ||
34 | * D7-D5: version | ||
35 | * D4-D0: chip id | ||
36 | * 11101 - CS4235 | ||
37 | * 01011 - CS4236B | ||
38 | * 01000 - CS4237B | ||
39 | * 01001 - CS4238B | ||
40 | * 11110 - CS4239 | ||
41 | * | ||
42 | * C2 | ||
43 | * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) | ||
44 | * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) | ||
45 | * | ||
46 | * C3 | ||
47 | * D7: 3D Enable (CS4237B) | ||
48 | * D6: 3D Mono Enable (CS4237B) | ||
49 | * D5: 3D Serial Output (CS4237B,CS4238B) | ||
50 | * D4: 3D Enable (CS4235,CS4238B,CS4239) | ||
51 | * | ||
52 | * C4 | ||
53 | * D7: consumer serial port enable (CS4237B,CS4238B) | ||
54 | * D6: channels status block reset (CS4237B,CS4238B) | ||
55 | * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) | ||
56 | * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B) | ||
57 | * | ||
58 | * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) | ||
59 | * D7-D6: first two bits of category code | ||
60 | * D5: lock | ||
61 | * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) | ||
62 | * D2: copy/copyright (0 = copy inhibited) | ||
63 | * D1: 0 = digital audio / 1 = non-digital audio | ||
64 | * | ||
65 | * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) | ||
66 | * D7-D6: sample frequency (0 = 44.1kHz) | ||
67 | * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) | ||
68 | * D4-D0: category code (upper bits) | ||
69 | * | ||
70 | * C7 reserved (must write 0) | ||
71 | * | ||
72 | * C8 wavetable control | ||
73 | * D7: volume control interrupt enable (CS4235,CS4239) | ||
74 | * D6: hardware volume control format (CS4235,CS4239) | ||
75 | * D3: wavetable serial port enable (all chips) | ||
76 | * D2: DSP serial port switch (all chips) | ||
77 | * D1: disable MCLK (all chips) | ||
78 | * D0: force BRESET low (all chips) | ||
79 | * | ||
80 | */ | ||
81 | |||
82 | #include <sound/driver.h> | ||
83 | #include <asm/io.h> | ||
84 | #include <linux/delay.h> | ||
85 | #include <linux/init.h> | ||
86 | #include <linux/time.h> | ||
87 | #include <linux/wait.h> | ||
88 | #include <sound/core.h> | ||
89 | #include <sound/cs4231.h> | ||
90 | #include <sound/asoundef.h> | ||
91 | |||
92 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
93 | MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips"); | ||
94 | MODULE_LICENSE("GPL"); | ||
95 | |||
96 | /* | ||
97 | * | ||
98 | */ | ||
99 | |||
100 | static unsigned char snd_cs4236_ext_map[18] = { | ||
101 | /* CS4236_LEFT_LINE */ 0xff, | ||
102 | /* CS4236_RIGHT_LINE */ 0xff, | ||
103 | /* CS4236_LEFT_MIC */ 0xdf, | ||
104 | /* CS4236_RIGHT_MIC */ 0xdf, | ||
105 | /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, | ||
106 | /* CS4236_RIGHT_MIX_CTRL */ 0xe0, | ||
107 | /* CS4236_LEFT_FM */ 0xbf, | ||
108 | /* CS4236_RIGHT_FM */ 0xbf, | ||
109 | /* CS4236_LEFT_DSP */ 0xbf, | ||
110 | /* CS4236_RIGHT_DSP */ 0xbf, | ||
111 | /* CS4236_RIGHT_LOOPBACK */ 0xbf, | ||
112 | /* CS4236_DAC_MUTE */ 0xe0, | ||
113 | /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ | ||
114 | /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ | ||
115 | /* CS4236_LEFT_MASTER */ 0xbf, | ||
116 | /* CS4236_RIGHT_MASTER */ 0xbf, | ||
117 | /* CS4236_LEFT_WAVE */ 0xbf, | ||
118 | /* CS4236_RIGHT_WAVE */ 0xbf | ||
119 | }; | ||
120 | |||
121 | /* | ||
122 | * | ||
123 | */ | ||
124 | |||
125 | static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val) | ||
126 | { | ||
127 | outb(reg, chip->cport + 3); | ||
128 | outb(chip->cimage[reg] = val, chip->cport + 4); | ||
129 | } | ||
130 | |||
131 | static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg) | ||
132 | { | ||
133 | outb(reg, chip->cport + 3); | ||
134 | return inb(chip->cport + 4); | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * PCM | ||
139 | */ | ||
140 | |||
141 | #define CLOCKS 8 | ||
142 | |||
143 | static ratnum_t clocks[CLOCKS] = { | ||
144 | { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 }, | ||
145 | { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 }, | ||
146 | { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 }, | ||
147 | { .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 }, | ||
148 | { .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 }, | ||
149 | { .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 }, | ||
150 | { .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 }, | ||
151 | { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 } | ||
152 | }; | ||
153 | |||
154 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { | ||
155 | .nrats = CLOCKS, | ||
156 | .rats = clocks, | ||
157 | }; | ||
158 | |||
159 | static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime) | ||
160 | { | ||
161 | return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
162 | &hw_constraints_clocks); | ||
163 | } | ||
164 | |||
165 | static unsigned char divisor_to_rate_register(unsigned int divisor) | ||
166 | { | ||
167 | switch (divisor) { | ||
168 | case 353: return 1; | ||
169 | case 529: return 2; | ||
170 | case 617: return 3; | ||
171 | case 1058: return 4; | ||
172 | case 1764: return 5; | ||
173 | case 2117: return 6; | ||
174 | case 2558: return 7; | ||
175 | default: | ||
176 | snd_runtime_check(divisor >= 21 && divisor <= 192, return 192); | ||
177 | return divisor; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr) | ||
182 | { | ||
183 | unsigned long flags; | ||
184 | unsigned char rate = divisor_to_rate_register(params->rate_den); | ||
185 | |||
186 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
187 | /* set fast playback format change and clean playback FIFO */ | ||
188 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); | ||
189 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); | ||
190 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10); | ||
191 | snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); | ||
192 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
193 | } | ||
194 | |||
195 | static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr) | ||
196 | { | ||
197 | unsigned long flags; | ||
198 | unsigned char rate = divisor_to_rate_register(params->rate_den); | ||
199 | |||
200 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
201 | /* set fast capture format change and clean capture FIFO */ | ||
202 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); | ||
203 | snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); | ||
204 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20); | ||
205 | snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); | ||
206 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
207 | } | ||
208 | |||
209 | #ifdef CONFIG_PM | ||
210 | |||
211 | static void snd_cs4236_suspend(cs4231_t *chip) | ||
212 | { | ||
213 | int reg; | ||
214 | unsigned long flags; | ||
215 | |||
216 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
217 | for (reg = 0; reg < 32; reg++) | ||
218 | chip->image[reg] = snd_cs4231_in(chip, reg); | ||
219 | for (reg = 0; reg < 18; reg++) | ||
220 | chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); | ||
221 | for (reg = 2; reg < 9; reg++) | ||
222 | chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); | ||
223 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
224 | } | ||
225 | |||
226 | static void snd_cs4236_resume(cs4231_t *chip) | ||
227 | { | ||
228 | int reg; | ||
229 | unsigned long flags; | ||
230 | |||
231 | snd_cs4231_mce_up(chip); | ||
232 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
233 | for (reg = 0; reg < 32; reg++) { | ||
234 | switch (reg) { | ||
235 | case CS4236_EXT_REG: | ||
236 | case CS4231_VERSION: | ||
237 | case 27: /* why? CS4235 - master left */ | ||
238 | case 29: /* why? CS4235 - master right */ | ||
239 | break; | ||
240 | default: | ||
241 | snd_cs4231_out(chip, reg, chip->image[reg]); | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | for (reg = 0; reg < 18; reg++) | ||
246 | snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); | ||
247 | for (reg = 2; reg < 9; reg++) { | ||
248 | switch (reg) { | ||
249 | case 7: | ||
250 | break; | ||
251 | default: | ||
252 | snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); | ||
253 | } | ||
254 | } | ||
255 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
256 | snd_cs4231_mce_down(chip); | ||
257 | } | ||
258 | |||
259 | #endif /* CONFIG_PM */ | ||
260 | |||
261 | int snd_cs4236_create(snd_card_t * card, | ||
262 | unsigned long port, | ||
263 | unsigned long cport, | ||
264 | int irq, int dma1, int dma2, | ||
265 | unsigned short hardware, | ||
266 | unsigned short hwshare, | ||
267 | cs4231_t ** rchip) | ||
268 | { | ||
269 | cs4231_t *chip; | ||
270 | unsigned char ver1, ver2; | ||
271 | unsigned int reg; | ||
272 | int err; | ||
273 | |||
274 | *rchip = NULL; | ||
275 | if (hardware == CS4231_HW_DETECT) | ||
276 | hardware = CS4231_HW_DETECT3; | ||
277 | if (cport < 0x100) { | ||
278 | snd_printk("please, specify control port for CS4236+ chips\n"); | ||
279 | return -ENODEV; | ||
280 | } | ||
281 | if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0) | ||
282 | return err; | ||
283 | |||
284 | if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) { | ||
285 | snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%x\n",chip->hardware); | ||
286 | snd_device_free(card, chip); | ||
287 | return -ENODEV; | ||
288 | } | ||
289 | #if 0 | ||
290 | { | ||
291 | int idx; | ||
292 | for (idx = 0; idx < 8; idx++) | ||
293 | snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx)); | ||
294 | for (idx = 0; idx < 9; idx++) | ||
295 | snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx)); | ||
296 | } | ||
297 | #endif | ||
298 | ver1 = snd_cs4236_ctrl_in(chip, 1); | ||
299 | ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); | ||
300 | snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2); | ||
301 | if (ver1 != ver2) { | ||
302 | snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport); | ||
303 | snd_device_free(card, chip); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | snd_cs4236_ctrl_out(chip, 0, 0x00); | ||
307 | snd_cs4236_ctrl_out(chip, 2, 0xff); | ||
308 | snd_cs4236_ctrl_out(chip, 3, 0x00); | ||
309 | snd_cs4236_ctrl_out(chip, 4, 0x80); | ||
310 | snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE); | ||
311 | snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); | ||
312 | snd_cs4236_ctrl_out(chip, 7, 0x00); | ||
313 | /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */ | ||
314 | /* is working with this setup, other hardware should have */ | ||
315 | /* different signal paths and this value should be selectable */ | ||
316 | /* in the future */ | ||
317 | snd_cs4236_ctrl_out(chip, 8, 0x8c); | ||
318 | chip->rate_constraint = snd_cs4236_xrate; | ||
319 | chip->set_playback_format = snd_cs4236_playback_format; | ||
320 | chip->set_capture_format = snd_cs4236_capture_format; | ||
321 | #ifdef CONFIG_PM | ||
322 | chip->suspend = snd_cs4236_suspend; | ||
323 | chip->resume = snd_cs4236_resume; | ||
324 | #endif | ||
325 | |||
326 | /* initialize extended registers */ | ||
327 | for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) | ||
328 | snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]); | ||
329 | |||
330 | /* initialize compatible but more featured registers */ | ||
331 | snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40); | ||
332 | snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40); | ||
333 | snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); | ||
334 | snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); | ||
335 | snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); | ||
336 | snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); | ||
337 | snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); | ||
338 | snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff); | ||
339 | snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); | ||
340 | switch (chip->hardware) { | ||
341 | case CS4231_HW_CS4235: | ||
342 | case CS4231_HW_CS4239: | ||
343 | snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff); | ||
344 | snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff); | ||
345 | break; | ||
346 | } | ||
347 | |||
348 | *rchip = chip; | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | int snd_cs4236_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) | ||
353 | { | ||
354 | snd_pcm_t *pcm; | ||
355 | int err; | ||
356 | |||
357 | if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0) | ||
358 | return err; | ||
359 | pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
360 | if (rpcm) | ||
361 | *rpcm = pcm; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | /* | ||
366 | * MIXER | ||
367 | */ | ||
368 | |||
369 | #define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
370 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
371 | .info = snd_cs4236_info_single, \ | ||
372 | .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \ | ||
373 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
374 | |||
375 | static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
376 | { | ||
377 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
378 | |||
379 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
380 | uinfo->count = 1; | ||
381 | uinfo->value.integer.min = 0; | ||
382 | uinfo->value.integer.max = mask; | ||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static int snd_cs4236_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
387 | { | ||
388 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
389 | unsigned long flags; | ||
390 | int reg = kcontrol->private_value & 0xff; | ||
391 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
392 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
393 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
394 | |||
395 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
396 | ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask; | ||
397 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
398 | if (invert) | ||
399 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int snd_cs4236_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
404 | { | ||
405 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
406 | unsigned long flags; | ||
407 | int reg = kcontrol->private_value & 0xff; | ||
408 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
409 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
410 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
411 | int change; | ||
412 | unsigned short val; | ||
413 | |||
414 | val = (ucontrol->value.integer.value[0] & mask); | ||
415 | if (invert) | ||
416 | val = mask - val; | ||
417 | val <<= shift; | ||
418 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
419 | val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val; | ||
420 | change = val != chip->eimage[CS4236_REG(reg)]; | ||
421 | snd_cs4236_ext_out(chip, reg, val); | ||
422 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
423 | return change; | ||
424 | } | ||
425 | |||
426 | #define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \ | ||
427 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
428 | .info = snd_cs4236_info_single, \ | ||
429 | .get = snd_cs4236_get_singlec, .put = snd_cs4236_put_singlec, \ | ||
430 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
431 | |||
432 | static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
433 | { | ||
434 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
435 | unsigned long flags; | ||
436 | int reg = kcontrol->private_value & 0xff; | ||
437 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
438 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
439 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
440 | |||
441 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
442 | ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask; | ||
443 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
444 | if (invert) | ||
445 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int snd_cs4236_put_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
450 | { | ||
451 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
452 | unsigned long flags; | ||
453 | int reg = kcontrol->private_value & 0xff; | ||
454 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
455 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
456 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
457 | int change; | ||
458 | unsigned short val; | ||
459 | |||
460 | val = (ucontrol->value.integer.value[0] & mask); | ||
461 | if (invert) | ||
462 | val = mask - val; | ||
463 | val <<= shift; | ||
464 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
465 | val = (chip->cimage[reg] & ~(mask << shift)) | val; | ||
466 | change = val != chip->cimage[reg]; | ||
467 | snd_cs4236_ctrl_out(chip, reg, val); | ||
468 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
469 | return change; | ||
470 | } | ||
471 | |||
472 | #define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
473 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
474 | .info = snd_cs4236_info_double, \ | ||
475 | .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \ | ||
476 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
477 | |||
478 | static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
479 | { | ||
480 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
481 | |||
482 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
483 | uinfo->count = 2; | ||
484 | uinfo->value.integer.min = 0; | ||
485 | uinfo->value.integer.max = mask; | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int snd_cs4236_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
490 | { | ||
491 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
492 | unsigned long flags; | ||
493 | int left_reg = kcontrol->private_value & 0xff; | ||
494 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
495 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
496 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
497 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
498 | int invert = (kcontrol->private_value >> 22) & 1; | ||
499 | |||
500 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
501 | ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask; | ||
502 | ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; | ||
503 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
504 | if (invert) { | ||
505 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
506 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
507 | } | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static int snd_cs4236_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
512 | { | ||
513 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
514 | unsigned long flags; | ||
515 | int left_reg = kcontrol->private_value & 0xff; | ||
516 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
517 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
518 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
519 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
520 | int invert = (kcontrol->private_value >> 22) & 1; | ||
521 | int change; | ||
522 | unsigned short val1, val2; | ||
523 | |||
524 | val1 = ucontrol->value.integer.value[0] & mask; | ||
525 | val2 = ucontrol->value.integer.value[1] & mask; | ||
526 | if (invert) { | ||
527 | val1 = mask - val1; | ||
528 | val2 = mask - val2; | ||
529 | } | ||
530 | val1 <<= shift_left; | ||
531 | val2 <<= shift_right; | ||
532 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
533 | if (left_reg != right_reg) { | ||
534 | val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1; | ||
535 | val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; | ||
536 | change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)]; | ||
537 | snd_cs4236_ext_out(chip, left_reg, val1); | ||
538 | snd_cs4236_ext_out(chip, right_reg, val2); | ||
539 | } else { | ||
540 | val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
541 | change = val1 != chip->eimage[CS4236_REG(left_reg)]; | ||
542 | snd_cs4236_ext_out(chip, left_reg, val1); | ||
543 | } | ||
544 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
545 | return change; | ||
546 | } | ||
547 | |||
548 | #define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
549 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
550 | .info = snd_cs4236_info_double, \ | ||
551 | .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \ | ||
552 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
553 | |||
554 | static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
555 | { | ||
556 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
557 | unsigned long flags; | ||
558 | int left_reg = kcontrol->private_value & 0xff; | ||
559 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
560 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
561 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
562 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
563 | int invert = (kcontrol->private_value >> 22) & 1; | ||
564 | |||
565 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
566 | ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; | ||
567 | ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; | ||
568 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
569 | if (invert) { | ||
570 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
571 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
572 | } | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | static int snd_cs4236_put_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
577 | { | ||
578 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
579 | unsigned long flags; | ||
580 | int left_reg = kcontrol->private_value & 0xff; | ||
581 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
582 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
583 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
584 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
585 | int invert = (kcontrol->private_value >> 22) & 1; | ||
586 | int change; | ||
587 | unsigned short val1, val2; | ||
588 | |||
589 | val1 = ucontrol->value.integer.value[0] & mask; | ||
590 | val2 = ucontrol->value.integer.value[1] & mask; | ||
591 | if (invert) { | ||
592 | val1 = mask - val1; | ||
593 | val2 = mask - val2; | ||
594 | } | ||
595 | val1 <<= shift_left; | ||
596 | val2 <<= shift_right; | ||
597 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
598 | val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; | ||
599 | val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; | ||
600 | change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; | ||
601 | snd_cs4231_out(chip, left_reg, val1); | ||
602 | snd_cs4236_ext_out(chip, right_reg, val2); | ||
603 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
604 | return change; | ||
605 | } | ||
606 | |||
607 | #define CS4236_MASTER_DIGITAL(xname, xindex) \ | ||
608 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
609 | .info = snd_cs4236_info_double, \ | ||
610 | .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \ | ||
611 | .private_value = 71 << 24 } | ||
612 | |||
613 | static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol) | ||
614 | { | ||
615 | return (vol < 64) ? 63 - vol : 64 + (71 - vol); | ||
616 | } | ||
617 | |||
618 | static int snd_cs4236_get_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
619 | { | ||
620 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
621 | unsigned long flags; | ||
622 | |||
623 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
624 | ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f); | ||
625 | ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f); | ||
626 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int snd_cs4236_put_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
631 | { | ||
632 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
633 | unsigned long flags; | ||
634 | int change; | ||
635 | unsigned short val1, val2; | ||
636 | |||
637 | val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f); | ||
638 | val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f); | ||
639 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
640 | val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1; | ||
641 | val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2; | ||
642 | change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)]; | ||
643 | snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1); | ||
644 | snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val1); | ||
645 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
646 | return change; | ||
647 | } | ||
648 | |||
649 | #define CS4235_OUTPUT_ACCU(xname, xindex) \ | ||
650 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
651 | .info = snd_cs4236_info_double, \ | ||
652 | .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \ | ||
653 | .private_value = 3 << 24 } | ||
654 | |||
655 | static inline int snd_cs4235_mixer_output_accu_get_volume(int vol) | ||
656 | { | ||
657 | switch ((vol >> 5) & 3) { | ||
658 | case 0: return 1; | ||
659 | case 1: return 3; | ||
660 | case 2: return 2; | ||
661 | case 3: return 0; | ||
662 | } | ||
663 | return 3; | ||
664 | } | ||
665 | |||
666 | static inline int snd_cs4235_mixer_output_accu_set_volume(int vol) | ||
667 | { | ||
668 | switch (vol & 3) { | ||
669 | case 0: return 3 << 5; | ||
670 | case 1: return 0 << 5; | ||
671 | case 2: return 2 << 5; | ||
672 | case 3: return 1 << 5; | ||
673 | } | ||
674 | return 1 << 5; | ||
675 | } | ||
676 | |||
677 | static int snd_cs4235_get_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
678 | { | ||
679 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
680 | unsigned long flags; | ||
681 | |||
682 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
683 | ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]); | ||
684 | ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]); | ||
685 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int snd_cs4235_put_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
690 | { | ||
691 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
692 | unsigned long flags; | ||
693 | int change; | ||
694 | unsigned short val1, val2; | ||
695 | |||
696 | val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]); | ||
697 | val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]); | ||
698 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
699 | val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1; | ||
700 | val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2; | ||
701 | change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; | ||
702 | snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1); | ||
703 | snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2); | ||
704 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
705 | return change; | ||
706 | } | ||
707 | |||
708 | static snd_kcontrol_new_t snd_cs4236_controls[] = { | ||
709 | |||
710 | CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), | ||
711 | CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), | ||
712 | CS4236_MASTER_DIGITAL("Master Digital Volume", 0), | ||
713 | |||
714 | CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), | ||
715 | |||
716 | CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
717 | CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), | ||
718 | |||
719 | CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), | ||
720 | CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1), | ||
721 | |||
722 | CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), | ||
723 | CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1), | ||
724 | |||
725 | CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), | ||
726 | CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1), | ||
727 | |||
728 | CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), | ||
729 | CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), | ||
730 | CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), | ||
731 | CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), | ||
732 | |||
733 | CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), | ||
734 | CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), | ||
735 | CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1), | ||
736 | CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), | ||
737 | |||
738 | CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), | ||
739 | CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), | ||
740 | CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), | ||
741 | CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), | ||
742 | |||
743 | CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), | ||
744 | CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), | ||
745 | CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), | ||
746 | |||
747 | CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), | ||
748 | CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), | ||
749 | CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), | ||
750 | CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), | ||
751 | |||
752 | CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), | ||
753 | CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), | ||
754 | |||
755 | CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), | ||
756 | CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1) | ||
757 | }; | ||
758 | |||
759 | static snd_kcontrol_new_t snd_cs4235_controls[] = { | ||
760 | |||
761 | CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), | ||
762 | CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1), | ||
763 | |||
764 | CS4235_OUTPUT_ACCU("Playback Volume", 0), | ||
765 | |||
766 | CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), | ||
767 | CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), | ||
768 | CS4236_MASTER_DIGITAL("Master Digital Volume", 0), | ||
769 | |||
770 | CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), | ||
771 | CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), | ||
772 | CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), | ||
773 | |||
774 | CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), | ||
775 | |||
776 | CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
777 | CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), | ||
778 | |||
779 | CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), | ||
780 | |||
781 | CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), | ||
782 | |||
783 | CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), | ||
784 | |||
785 | CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), | ||
786 | CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), | ||
787 | CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1), | ||
788 | CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0), | ||
789 | |||
790 | CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), | ||
791 | CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), | ||
792 | CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), | ||
793 | |||
794 | CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), | ||
795 | CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), | ||
796 | CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), | ||
797 | |||
798 | CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), | ||
799 | |||
800 | CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), | ||
801 | CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), | ||
802 | |||
803 | CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), | ||
804 | }; | ||
805 | |||
806 | #define CS4236_IEC958_ENABLE(xname, xindex) \ | ||
807 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
808 | .info = snd_cs4236_info_single, \ | ||
809 | .get = snd_cs4236_get_iec958_switch, .put = snd_cs4236_put_iec958_switch, \ | ||
810 | .private_value = 1 << 16 } | ||
811 | |||
812 | static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
813 | { | ||
814 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
815 | unsigned long flags; | ||
816 | |||
817 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
818 | ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0; | ||
819 | #if 0 | ||
820 | printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", | ||
821 | snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), | ||
822 | snd_cs4236_ctrl_in(chip, 3), | ||
823 | snd_cs4236_ctrl_in(chip, 4), | ||
824 | snd_cs4236_ctrl_in(chip, 5), | ||
825 | snd_cs4236_ctrl_in(chip, 6), | ||
826 | snd_cs4236_ctrl_in(chip, 8)); | ||
827 | #endif | ||
828 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
829 | return 0; | ||
830 | } | ||
831 | |||
832 | static int snd_cs4236_put_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
833 | { | ||
834 | cs4231_t *chip = snd_kcontrol_chip(kcontrol); | ||
835 | unsigned long flags; | ||
836 | int change; | ||
837 | unsigned short enable, val; | ||
838 | |||
839 | enable = ucontrol->value.integer.value[0] & 1; | ||
840 | |||
841 | down(&chip->mce_mutex); | ||
842 | snd_cs4231_mce_up(chip); | ||
843 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
844 | val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1); | ||
845 | change = val != chip->image[CS4231_ALT_FEATURE_1]; | ||
846 | snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val); | ||
847 | val = snd_cs4236_ctrl_in(chip, 4) | 0xc0; | ||
848 | snd_cs4236_ctrl_out(chip, 4, val); | ||
849 | udelay(100); | ||
850 | val &= ~0x40; | ||
851 | snd_cs4236_ctrl_out(chip, 4, val); | ||
852 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
853 | snd_cs4231_mce_down(chip); | ||
854 | up(&chip->mce_mutex); | ||
855 | |||
856 | #if 0 | ||
857 | printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", | ||
858 | snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), | ||
859 | snd_cs4236_ctrl_in(chip, 3), | ||
860 | snd_cs4236_ctrl_in(chip, 4), | ||
861 | snd_cs4236_ctrl_in(chip, 5), | ||
862 | snd_cs4236_ctrl_in(chip, 6), | ||
863 | snd_cs4236_ctrl_in(chip, 8)); | ||
864 | #endif | ||
865 | return change; | ||
866 | } | ||
867 | |||
868 | static snd_kcontrol_new_t snd_cs4236_iec958_controls[] = { | ||
869 | CS4236_IEC958_ENABLE("IEC958 Output Enable", 0), | ||
870 | CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0), | ||
871 | CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0), | ||
872 | CS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0), | ||
873 | CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0), | ||
874 | CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0) | ||
875 | }; | ||
876 | |||
877 | static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4235[] = { | ||
878 | CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), | ||
879 | CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1) | ||
880 | }; | ||
881 | |||
882 | static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4237[] = { | ||
883 | CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0), | ||
884 | CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), | ||
885 | CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1), | ||
886 | CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0), | ||
887 | CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) | ||
888 | }; | ||
889 | |||
890 | static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4238[] = { | ||
891 | CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), | ||
892 | CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), | ||
893 | CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1), | ||
894 | CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) | ||
895 | }; | ||
896 | |||
897 | int snd_cs4236_mixer(cs4231_t *chip) | ||
898 | { | ||
899 | snd_card_t *card; | ||
900 | unsigned int idx, count; | ||
901 | int err; | ||
902 | snd_kcontrol_new_t *kcontrol; | ||
903 | |||
904 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
905 | card = chip->card; | ||
906 | strcpy(card->mixername, snd_cs4231_chip_id(chip)); | ||
907 | |||
908 | if (chip->hardware == CS4231_HW_CS4235 || | ||
909 | chip->hardware == CS4231_HW_CS4239) { | ||
910 | for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) { | ||
911 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0) | ||
912 | return err; | ||
913 | } | ||
914 | } else { | ||
915 | for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) { | ||
916 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0) | ||
917 | return err; | ||
918 | } | ||
919 | } | ||
920 | switch (chip->hardware) { | ||
921 | case CS4231_HW_CS4235: | ||
922 | case CS4231_HW_CS4239: | ||
923 | count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235); | ||
924 | kcontrol = snd_cs4236_3d_controls_cs4235; | ||
925 | break; | ||
926 | case CS4231_HW_CS4237B: | ||
927 | count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237); | ||
928 | kcontrol = snd_cs4236_3d_controls_cs4237; | ||
929 | break; | ||
930 | case CS4231_HW_CS4238B: | ||
931 | count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238); | ||
932 | kcontrol = snd_cs4236_3d_controls_cs4238; | ||
933 | break; | ||
934 | default: | ||
935 | count = 0; | ||
936 | kcontrol = NULL; | ||
937 | } | ||
938 | for (idx = 0; idx < count; idx++, kcontrol++) { | ||
939 | if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0) | ||
940 | return err; | ||
941 | } | ||
942 | if (chip->hardware == CS4231_HW_CS4237B || | ||
943 | chip->hardware == CS4231_HW_CS4238B) { | ||
944 | for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) { | ||
945 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0) | ||
946 | return err; | ||
947 | } | ||
948 | } | ||
949 | return 0; | ||
950 | } | ||
951 | |||
952 | EXPORT_SYMBOL(snd_cs4236_create); | ||
953 | EXPORT_SYMBOL(snd_cs4236_pcm); | ||
954 | EXPORT_SYMBOL(snd_cs4236_mixer); | ||
955 | |||
956 | /* | ||
957 | * INIT part | ||
958 | */ | ||
959 | |||
960 | static int __init alsa_cs4236_init(void) | ||
961 | { | ||
962 | return 0; | ||
963 | } | ||
964 | |||
965 | static void __exit alsa_cs4236_exit(void) | ||
966 | { | ||
967 | } | ||
968 | |||
969 | module_init(alsa_cs4236_init) | ||
970 | module_exit(alsa_cs4236_exit) | ||
diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c new file mode 100644 index 000000000000..db7c3397b323 --- /dev/null +++ b/sound/isa/dt019x.c | |||
@@ -0,0 +1,327 @@ | |||
1 | |||
2 | /* | ||
3 | dt019x.c - driver for Diamond Technologies DT-0197H based soundcards. | ||
4 | Copyright (C) 1999, 2002 by Massimo Piccioni <dafastidio@libero.it> | ||
5 | |||
6 | Generalised for soundcards based on DT-0196 and ALS-007 chips | ||
7 | by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>: June 2002. | ||
8 | |||
9 | This program is free software; you can redistribute it and/or modify | ||
10 | it under the terms of the GNU General Public License as published by | ||
11 | the Free Software Foundation; either version 2 of the License, or | ||
12 | (at your option) any later version. | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, | ||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software | ||
21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/pnp.h> | ||
29 | #include <linux/moduleparam.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/initval.h> | ||
32 | #include <sound/mpu401.h> | ||
33 | #include <sound/opl3.h> | ||
34 | #include <sound/sb.h> | ||
35 | |||
36 | #define PFX "dt019x: " | ||
37 | |||
38 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
39 | MODULE_DESCRIPTION("Diamond Technologies DT-019X / Avance Logic ALS-007"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | MODULE_SUPPORTED_DEVICE("{{Diamond Technologies DT-019X}," | ||
42 | "{Avance Logic ALS-007}}"); | ||
43 | |||
44 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
45 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
46 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
47 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
48 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
49 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
50 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ | ||
51 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ | ||
52 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
53 | |||
54 | module_param_array(index, int, NULL, 0444); | ||
55 | MODULE_PARM_DESC(index, "Index value for DT-019X based soundcard."); | ||
56 | module_param_array(id, charp, NULL, 0444); | ||
57 | MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard."); | ||
58 | module_param_array(enable, bool, NULL, 0444); | ||
59 | MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard."); | ||
60 | module_param_array(port, long, NULL, 0444); | ||
61 | MODULE_PARM_DESC(port, "Port # for dt019x driver."); | ||
62 | module_param_array(mpu_port, long, NULL, 0444); | ||
63 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for dt019x driver."); | ||
64 | module_param_array(fm_port, long, NULL, 0444); | ||
65 | MODULE_PARM_DESC(fm_port, "FM port # for dt019x driver."); | ||
66 | module_param_array(irq, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(irq, "IRQ # for dt019x driver."); | ||
68 | module_param_array(mpu_irq, int, NULL, 0444); | ||
69 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for dt019x driver."); | ||
70 | module_param_array(dma8, int, NULL, 0444); | ||
71 | MODULE_PARM_DESC(dma8, "8-bit DMA # for dt019x driver."); | ||
72 | |||
73 | struct snd_card_dt019x { | ||
74 | struct pnp_dev *dev; | ||
75 | struct pnp_dev *devmpu; | ||
76 | struct pnp_dev *devopl; | ||
77 | }; | ||
78 | |||
79 | static struct pnp_card_device_id snd_dt019x_pnpids[] = { | ||
80 | /* DT197A30 */ | ||
81 | { .id = "RWB1688", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" }, } }, | ||
82 | /* DT0196 / ALS-007 */ | ||
83 | { .id = "ALS0007", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" }, } }, | ||
84 | { .id = "", } | ||
85 | }; | ||
86 | |||
87 | MODULE_DEVICE_TABLE(pnp_card, snd_dt019x_pnpids); | ||
88 | |||
89 | |||
90 | #define DRIVER_NAME "snd-card-dt019x" | ||
91 | |||
92 | |||
93 | static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard, | ||
94 | struct pnp_card_link *card, | ||
95 | const struct pnp_card_device_id *pid) | ||
96 | { | ||
97 | struct pnp_dev *pdev; | ||
98 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
99 | int err; | ||
100 | |||
101 | if (!cfg) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | acard->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); | ||
105 | if (acard->dev == NULL) { | ||
106 | kfree (cfg); | ||
107 | return -ENODEV; | ||
108 | } | ||
109 | acard->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); | ||
110 | acard->devopl = pnp_request_card_device(card, pid->devs[2].id, NULL); | ||
111 | |||
112 | pdev = acard->dev; | ||
113 | pnp_init_resource_table(cfg); | ||
114 | |||
115 | if (port[dev] != SNDRV_AUTO_PORT) | ||
116 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
117 | if (dma8[dev] != SNDRV_AUTO_DMA) | ||
118 | pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); | ||
119 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
120 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
121 | |||
122 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
123 | snd_printk(KERN_ERR PFX "DT-019X AUDIO the requested resources are invalid, using auto config\n"); | ||
124 | err = pnp_activate_dev(pdev); | ||
125 | if (err < 0) { | ||
126 | snd_printk(KERN_ERR PFX "DT-019X AUDIO pnp configure failure\n"); | ||
127 | kfree(cfg); | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | port[dev] = pnp_port_start(pdev, 0); | ||
132 | dma8[dev] = pnp_dma(pdev, 0); | ||
133 | irq[dev] = pnp_irq(pdev, 0); | ||
134 | snd_printdd("dt019x: found audio interface: port=0x%lx, irq=0x%x, dma=0x%x\n", | ||
135 | port[dev],irq[dev],dma8[dev]); | ||
136 | |||
137 | pdev = acard->devmpu; | ||
138 | |||
139 | if (pdev != NULL) { | ||
140 | pnp_init_resource_table(cfg); | ||
141 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
142 | pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); | ||
143 | if (mpu_irq[dev] != SNDRV_AUTO_IRQ) | ||
144 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); | ||
145 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
146 | snd_printk(KERN_ERR PFX "DT-019X MPU401 the requested resources are invalid, using auto config\n"); | ||
147 | err = pnp_activate_dev(pdev); | ||
148 | if (err < 0) { | ||
149 | pnp_release_card_device(pdev); | ||
150 | snd_printk(KERN_ERR PFX "DT-019X MPU401 pnp configure failure, skipping\n"); | ||
151 | goto __mpu_error; | ||
152 | } | ||
153 | mpu_port[dev] = pnp_port_start(pdev, 0); | ||
154 | mpu_irq[dev] = pnp_irq(pdev, 0); | ||
155 | snd_printdd("dt019x: found MPU-401: port=0x%lx, irq=0x%x\n", | ||
156 | mpu_port[dev],mpu_irq[dev]); | ||
157 | } else { | ||
158 | __mpu_error: | ||
159 | acard->devmpu = NULL; | ||
160 | mpu_port[dev] = -1; | ||
161 | } | ||
162 | |||
163 | pdev = acard->devopl; | ||
164 | if (pdev != NULL) { | ||
165 | pnp_init_resource_table(cfg); | ||
166 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
167 | pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4); | ||
168 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
169 | snd_printk(KERN_ERR PFX "DT-019X OPL3 the requested resources are invalid, using auto config\n"); | ||
170 | err = pnp_activate_dev(pdev); | ||
171 | if (err < 0) { | ||
172 | pnp_release_card_device(pdev); | ||
173 | snd_printk(KERN_ERR PFX "DT-019X OPL3 pnp configure failure, skipping\n"); | ||
174 | goto __fm_error; | ||
175 | } | ||
176 | fm_port[dev] = pnp_port_start(pdev, 0); | ||
177 | snd_printdd("dt019x: found OPL3 synth: port=0x%lx\n",fm_port[dev]); | ||
178 | } else { | ||
179 | __fm_error: | ||
180 | acard->devopl = NULL; | ||
181 | fm_port[dev] = -1; | ||
182 | } | ||
183 | |||
184 | kfree(cfg); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int __devinit snd_card_dt019x_probe(int dev, struct pnp_card_link *pcard, const struct pnp_card_device_id *pid) | ||
189 | { | ||
190 | int error; | ||
191 | sb_t *chip; | ||
192 | snd_card_t *card; | ||
193 | struct snd_card_dt019x *acard; | ||
194 | opl3_t *opl3; | ||
195 | |||
196 | if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
197 | sizeof(struct snd_card_dt019x))) == NULL) | ||
198 | return -ENOMEM; | ||
199 | acard = (struct snd_card_dt019x *)card->private_data; | ||
200 | |||
201 | snd_card_set_dev(card, &pcard->card->dev); | ||
202 | if ((error = snd_card_dt019x_pnp(dev, acard, pcard, pid))) { | ||
203 | snd_card_free(card); | ||
204 | return error; | ||
205 | } | ||
206 | |||
207 | if ((error = snd_sbdsp_create(card, port[dev], | ||
208 | irq[dev], | ||
209 | snd_sb16dsp_interrupt, | ||
210 | dma8[dev], | ||
211 | -1, | ||
212 | SB_HW_DT019X, | ||
213 | &chip)) < 0) { | ||
214 | snd_card_free(card); | ||
215 | return error; | ||
216 | } | ||
217 | |||
218 | strcpy(card->driver, "DT-019X"); | ||
219 | strcpy(card->shortname, "Diamond Tech. DT-019X"); | ||
220 | sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d", | ||
221 | card->shortname, chip->name, chip->port, | ||
222 | irq[dev], dma8[dev]); | ||
223 | |||
224 | if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { | ||
225 | snd_card_free(card); | ||
226 | return error; | ||
227 | } | ||
228 | if ((error = snd_sbmixer_new(chip)) < 0) { | ||
229 | snd_card_free(card); | ||
230 | return error; | ||
231 | } | ||
232 | |||
233 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
234 | if (mpu_irq[dev] == SNDRV_AUTO_IRQ) | ||
235 | mpu_irq[dev] = -1; | ||
236 | if (snd_mpu401_uart_new(card, 0, | ||
237 | /* MPU401_HW_SB,*/ | ||
238 | MPU401_HW_MPU401, | ||
239 | mpu_port[dev], 0, | ||
240 | mpu_irq[dev], | ||
241 | mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, | ||
242 | NULL) < 0) | ||
243 | snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", mpu_port[dev]); | ||
244 | } | ||
245 | |||
246 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
247 | if (snd_opl3_create(card, | ||
248 | fm_port[dev], | ||
249 | fm_port[dev] + 2, | ||
250 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
251 | snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx ?\n", | ||
252 | fm_port[dev], fm_port[dev] + 2); | ||
253 | } else { | ||
254 | if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { | ||
255 | snd_card_free(card); | ||
256 | return error; | ||
257 | } | ||
258 | if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
259 | snd_card_free(card); | ||
260 | return error; | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | |||
265 | if ((error = snd_card_register(card)) < 0) { | ||
266 | snd_card_free(card); | ||
267 | return error; | ||
268 | } | ||
269 | pnp_set_card_drvdata(pcard, card); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int __devinit snd_dt019x_pnp_probe(struct pnp_card_link *card, | ||
274 | const struct pnp_card_device_id *pid) | ||
275 | { | ||
276 | static int dev; | ||
277 | int res; | ||
278 | |||
279 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
280 | if (!enable[dev]) | ||
281 | continue; | ||
282 | res = snd_card_dt019x_probe(dev, card, pid); | ||
283 | if (res < 0) | ||
284 | return res; | ||
285 | dev++; | ||
286 | return 0; | ||
287 | } | ||
288 | return -ENODEV; | ||
289 | } | ||
290 | |||
291 | static void __devexit snd_dt019x_pnp_remove(struct pnp_card_link * pcard) | ||
292 | { | ||
293 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
294 | snd_card_disconnect(card); | ||
295 | snd_card_free_in_thread(card); | ||
296 | } | ||
297 | |||
298 | static struct pnp_card_driver dt019x_pnpc_driver = { | ||
299 | .flags = PNP_DRIVER_RES_DISABLE, | ||
300 | .name = "dt019x", | ||
301 | .id_table = snd_dt019x_pnpids, | ||
302 | .probe = snd_dt019x_pnp_probe, | ||
303 | .remove = __devexit_p(snd_dt019x_pnp_remove), | ||
304 | }; | ||
305 | |||
306 | static int __init alsa_card_dt019x_init(void) | ||
307 | { | ||
308 | int cards = 0; | ||
309 | |||
310 | cards += pnp_register_card_driver(&dt019x_pnpc_driver); | ||
311 | |||
312 | #ifdef MODULE | ||
313 | if (!cards) { | ||
314 | pnp_unregister_card_driver(&dt019x_pnpc_driver); | ||
315 | snd_printk(KERN_ERR "no DT-019X / ALS-007 based soundcards found\n"); | ||
316 | } | ||
317 | #endif | ||
318 | return cards ? 0 : -ENODEV; | ||
319 | } | ||
320 | |||
321 | static void __exit alsa_card_dt019x_exit(void) | ||
322 | { | ||
323 | pnp_unregister_card_driver(&dt019x_pnpc_driver); | ||
324 | } | ||
325 | |||
326 | module_init(alsa_card_dt019x_init) | ||
327 | module_exit(alsa_card_dt019x_exit) | ||
diff --git a/sound/isa/es1688/Makefile b/sound/isa/es1688/Makefile new file mode 100644 index 000000000000..501c8bf903af --- /dev/null +++ b/sound/isa/es1688/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-es1688-lib-objs := es1688_lib.o | ||
7 | snd-es1688-objs := es1688.o | ||
8 | |||
9 | # Toplevel Module Dependency | ||
10 | obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o | ||
11 | obj-$(CONFIG_SND_GUSEXTREME) += snd-es1688-lib.o | ||
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c new file mode 100644 index 000000000000..c5eaec087b46 --- /dev/null +++ b/sound/isa/es1688/es1688.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * Driver for generic ESS AudioDrive ESx688 soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/es1688.h> | ||
30 | #include <sound/mpu401.h> | ||
31 | #include <sound/opl3.h> | ||
32 | #define SNDRV_LEGACY_AUTO_PROBE | ||
33 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
34 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
35 | #include <sound/initval.h> | ||
36 | |||
37 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
38 | MODULE_DESCRIPTION("ESS ESx688 AudioDrive"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," | ||
41 | "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," | ||
42 | "{ESS,ES688 AudioDrive,pnp:ESS6881}," | ||
43 | "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); | ||
44 | |||
45 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
46 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
47 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
48 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ | ||
49 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; | ||
50 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
51 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
52 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
53 | |||
54 | module_param_array(index, int, NULL, 0444); | ||
55 | MODULE_PARM_DESC(index, "Index value for ESx688 soundcard."); | ||
56 | module_param_array(id, charp, NULL, 0444); | ||
57 | MODULE_PARM_DESC(id, "ID string for ESx688 soundcard."); | ||
58 | module_param_array(enable, bool, NULL, 0444); | ||
59 | MODULE_PARM_DESC(enable, "Enable ESx688 soundcard."); | ||
60 | module_param_array(port, long, NULL, 0444); | ||
61 | MODULE_PARM_DESC(port, "Port # for ESx688 driver."); | ||
62 | module_param_array(mpu_port, long, NULL, 0444); | ||
63 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ESx688 driver."); | ||
64 | module_param_array(irq, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(irq, "IRQ # for ESx688 driver."); | ||
66 | module_param_array(mpu_irq, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ESx688 driver."); | ||
68 | module_param_array(dma8, int, NULL, 0444); | ||
69 | MODULE_PARM_DESC(dma8, "8-bit DMA # for ESx688 driver."); | ||
70 | |||
71 | static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
72 | |||
73 | |||
74 | static int __init snd_audiodrive_probe(int dev) | ||
75 | { | ||
76 | static int possible_irqs[] = {5, 9, 10, 7, -1}; | ||
77 | static int possible_dmas[] = {1, 3, 0, -1}; | ||
78 | int xirq, xdma, xmpu_irq; | ||
79 | snd_card_t *card; | ||
80 | es1688_t *chip; | ||
81 | opl3_t *opl3; | ||
82 | snd_pcm_t *pcm; | ||
83 | int err; | ||
84 | |||
85 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
86 | if (card == NULL) | ||
87 | return -ENOMEM; | ||
88 | |||
89 | xirq = irq[dev]; | ||
90 | if (xirq == SNDRV_AUTO_IRQ) { | ||
91 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
92 | snd_card_free(card); | ||
93 | snd_printk("unable to find a free IRQ\n"); | ||
94 | return -EBUSY; | ||
95 | } | ||
96 | } | ||
97 | xmpu_irq = mpu_irq[dev]; | ||
98 | xdma = dma8[dev]; | ||
99 | if (xdma == SNDRV_AUTO_DMA) { | ||
100 | if ((xdma = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
101 | snd_card_free(card); | ||
102 | snd_printk("unable to find a free DMA\n"); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | if ((err = snd_es1688_create(card, port[dev], mpu_port[dev], | ||
108 | xirq, xmpu_irq, xdma, | ||
109 | ES1688_HW_AUTO, &chip)) < 0) { | ||
110 | snd_card_free(card); | ||
111 | return err; | ||
112 | } | ||
113 | if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) { | ||
114 | snd_card_free(card); | ||
115 | return err; | ||
116 | } | ||
117 | if ((err = snd_es1688_mixer(chip)) < 0) { | ||
118 | snd_card_free(card); | ||
119 | return err; | ||
120 | } | ||
121 | |||
122 | strcpy(card->driver, "ES1688"); | ||
123 | strcpy(card->shortname, pcm->name); | ||
124 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, xirq, xdma); | ||
125 | |||
126 | if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { | ||
127 | printk(KERN_ERR "es1688: opl3 not detected at 0x%lx\n", chip->port); | ||
128 | } else { | ||
129 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
130 | snd_card_free(card); | ||
131 | return err; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | if (xmpu_irq >= 0 && xmpu_irq != SNDRV_AUTO_IRQ && chip->mpu_port > 0) { | ||
136 | if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, | ||
137 | chip->mpu_port, 0, | ||
138 | xmpu_irq, | ||
139 | SA_INTERRUPT, | ||
140 | NULL)) < 0) { | ||
141 | snd_card_free(card); | ||
142 | return err; | ||
143 | } | ||
144 | } | ||
145 | if ((err = snd_card_register(card)) < 0) { | ||
146 | snd_card_free(card); | ||
147 | return err; | ||
148 | } | ||
149 | snd_audiodrive_cards[dev] = card; | ||
150 | return 0; | ||
151 | |||
152 | } | ||
153 | |||
154 | static int __init snd_audiodrive_legacy_auto_probe(unsigned long xport) | ||
155 | { | ||
156 | static int dev; | ||
157 | int res; | ||
158 | |||
159 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
160 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
161 | continue; | ||
162 | port[dev] = xport; | ||
163 | res = snd_audiodrive_probe(dev); | ||
164 | if (res < 0) | ||
165 | port[dev] = SNDRV_AUTO_PORT; | ||
166 | return res; | ||
167 | } | ||
168 | return -ENODEV; | ||
169 | } | ||
170 | |||
171 | static int __init alsa_card_es1688_init(void) | ||
172 | { | ||
173 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; | ||
174 | int dev, cards = 0, i; | ||
175 | |||
176 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
177 | if (port[dev] == SNDRV_AUTO_PORT) | ||
178 | continue; | ||
179 | if (snd_audiodrive_probe(dev) >= 0) | ||
180 | cards++; | ||
181 | } | ||
182 | i = snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe); | ||
183 | if (i > 0) | ||
184 | cards += i; | ||
185 | |||
186 | if (!cards) { | ||
187 | #ifdef MODULE | ||
188 | printk(KERN_ERR "ESS AudioDrive ES1688 soundcard not found or device busy\n"); | ||
189 | #endif | ||
190 | return -ENODEV; | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static void __exit alsa_card_es1688_exit(void) | ||
196 | { | ||
197 | int idx; | ||
198 | |||
199 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
200 | snd_card_free(snd_audiodrive_cards[idx]); | ||
201 | } | ||
202 | |||
203 | module_init(alsa_card_es1688_init) | ||
204 | module_exit(alsa_card_es1688_exit) | ||
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c new file mode 100644 index 000000000000..17f68d07d9b2 --- /dev/null +++ b/sound/isa/es1688/es1688_lib.c | |||
@@ -0,0 +1,1062 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of ESS ES1688/688/488 chip | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/es1688.h> | ||
30 | #include <sound/initval.h> | ||
31 | |||
32 | #include <asm/io.h> | ||
33 | #include <asm/dma.h> | ||
34 | |||
35 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
36 | MODULE_DESCRIPTION("ESS ESx688 lowlevel module"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val) | ||
40 | { | ||
41 | int i; | ||
42 | |||
43 | for (i = 10000; i; i--) | ||
44 | if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { | ||
45 | outb(val, ES1688P(chip, COMMAND)); | ||
46 | return 1; | ||
47 | } | ||
48 | #ifdef CONFIG_SND_DEBUG | ||
49 | printk("snd_es1688_dsp_command: timeout (0x%x)\n", val); | ||
50 | #endif | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static int snd_es1688_dsp_get_byte(es1688_t *chip) | ||
55 | { | ||
56 | int i; | ||
57 | |||
58 | for (i = 1000; i; i--) | ||
59 | if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) | ||
60 | return inb(ES1688P(chip, READ)); | ||
61 | snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); | ||
62 | return -ENODEV; | ||
63 | } | ||
64 | |||
65 | static int snd_es1688_write(es1688_t *chip, | ||
66 | unsigned char reg, unsigned char data) | ||
67 | { | ||
68 | if (!snd_es1688_dsp_command(chip, reg)) | ||
69 | return 0; | ||
70 | return snd_es1688_dsp_command(chip, data); | ||
71 | } | ||
72 | |||
73 | static int snd_es1688_read(es1688_t *chip, unsigned char reg) | ||
74 | { | ||
75 | /* Read a byte from an extended mode register of ES1688 */ | ||
76 | if (!snd_es1688_dsp_command(chip, 0xc0)) | ||
77 | return -1; | ||
78 | if (!snd_es1688_dsp_command(chip, reg)) | ||
79 | return -1; | ||
80 | return snd_es1688_dsp_get_byte(chip); | ||
81 | } | ||
82 | |||
83 | void snd_es1688_mixer_write(es1688_t *chip, | ||
84 | unsigned char reg, unsigned char data) | ||
85 | { | ||
86 | outb(reg, ES1688P(chip, MIXER_ADDR)); | ||
87 | udelay(10); | ||
88 | outb(data, ES1688P(chip, MIXER_DATA)); | ||
89 | udelay(10); | ||
90 | } | ||
91 | |||
92 | static unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg) | ||
93 | { | ||
94 | unsigned char result; | ||
95 | |||
96 | outb(reg, ES1688P(chip, MIXER_ADDR)); | ||
97 | udelay(10); | ||
98 | result = inb(ES1688P(chip, MIXER_DATA)); | ||
99 | udelay(10); | ||
100 | return result; | ||
101 | } | ||
102 | |||
103 | static int snd_es1688_reset(es1688_t *chip) | ||
104 | { | ||
105 | int i; | ||
106 | |||
107 | outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ | ||
108 | udelay(10); | ||
109 | outb(0, ES1688P(chip, RESET)); | ||
110 | udelay(30); | ||
111 | for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); | ||
112 | if (inb(ES1688P(chip, READ)) != 0xaa) { | ||
113 | snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); | ||
114 | return -ENODEV; | ||
115 | } | ||
116 | snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int snd_es1688_probe(es1688_t *chip) | ||
121 | { | ||
122 | unsigned long flags; | ||
123 | unsigned short major, minor, hw; | ||
124 | int i; | ||
125 | |||
126 | /* | ||
127 | * initialization sequence | ||
128 | */ | ||
129 | |||
130 | spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ | ||
131 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
132 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
133 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
134 | inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ | ||
135 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
136 | inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ | ||
137 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
138 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
139 | inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ | ||
140 | inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ | ||
141 | inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ | ||
142 | |||
143 | if (snd_es1688_reset(chip) < 0) { | ||
144 | snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); | ||
145 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
146 | return -ENODEV; | ||
147 | } | ||
148 | snd_es1688_dsp_command(chip, 0xe7); /* return identification */ | ||
149 | |||
150 | for (i = 1000, major = minor = 0; i; i--) { | ||
151 | if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { | ||
152 | if (major == 0) { | ||
153 | major = inb(ES1688P(chip, READ)); | ||
154 | } else { | ||
155 | minor = inb(ES1688P(chip, READ)); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
161 | |||
162 | snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); | ||
163 | |||
164 | chip->version = (major << 8) | minor; | ||
165 | if (!chip->version) | ||
166 | return -ENODEV; /* probably SB */ | ||
167 | |||
168 | hw = ES1688_HW_AUTO; | ||
169 | switch (chip->version & 0xfff0) { | ||
170 | case 0x4880: | ||
171 | snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port); | ||
172 | return -ENODEV; | ||
173 | case 0x6880: | ||
174 | hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688; | ||
175 | break; | ||
176 | default: | ||
177 | snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version); | ||
178 | return -ENODEV; | ||
179 | } | ||
180 | |||
181 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
182 | snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ | ||
183 | snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ | ||
184 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
185 | |||
186 | /* enable joystick, but disable OPL3 */ | ||
187 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
188 | snd_es1688_mixer_write(chip, 0x40, 0x01); | ||
189 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int snd_es1688_init(es1688_t * chip, int enable) | ||
195 | { | ||
196 | static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; | ||
197 | unsigned long flags; | ||
198 | int cfg, irq_bits, dma, dma_bits, tmp, tmp1; | ||
199 | |||
200 | /* ok.. setup MPU-401 port and joystick and OPL3 */ | ||
201 | cfg = 0x01; /* enable joystick, but disable OPL3 */ | ||
202 | if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { | ||
203 | tmp = (chip->mpu_port & 0x0f0) >> 4; | ||
204 | if (tmp <= 3) { | ||
205 | switch (chip->mpu_irq) { | ||
206 | case 9: | ||
207 | tmp1 = 4; | ||
208 | break; | ||
209 | case 5: | ||
210 | tmp1 = 5; | ||
211 | break; | ||
212 | case 7: | ||
213 | tmp1 = 6; | ||
214 | break; | ||
215 | case 10: | ||
216 | tmp1 = 7; | ||
217 | break; | ||
218 | default: | ||
219 | tmp1 = 0; | ||
220 | } | ||
221 | if (tmp1) { | ||
222 | cfg |= (tmp << 3) | (tmp1 << 5); | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | #if 0 | ||
227 | snd_printk("mpu cfg = 0x%x\n", cfg); | ||
228 | #endif | ||
229 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
230 | snd_es1688_mixer_write(chip, 0x40, cfg); | ||
231 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
232 | /* --- */ | ||
233 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
234 | snd_es1688_read(chip, 0xb1); | ||
235 | snd_es1688_read(chip, 0xb2); | ||
236 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
237 | if (enable) { | ||
238 | cfg = 0xf0; /* enable only DMA counter interrupt */ | ||
239 | irq_bits = irqs[chip->irq & 0x0f]; | ||
240 | if (irq_bits < 0) { | ||
241 | snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq); | ||
242 | #if 0 | ||
243 | irq_bits = 0; | ||
244 | cfg = 0x10; | ||
245 | #endif | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
249 | snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); | ||
250 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
251 | cfg = 0xf0; /* extended mode DMA enable */ | ||
252 | dma = chip->dma8; | ||
253 | if (dma > 3 || dma == 2) { | ||
254 | snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma); | ||
255 | #if 0 | ||
256 | dma_bits = 0; | ||
257 | cfg = 0x00; /* disable all DMA */ | ||
258 | #endif | ||
259 | return -EINVAL; | ||
260 | } else { | ||
261 | dma_bits = dma; | ||
262 | if (dma != 3) | ||
263 | dma_bits++; | ||
264 | } | ||
265 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
266 | snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); | ||
267 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
268 | } else { | ||
269 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
270 | snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ | ||
271 | snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ | ||
272 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
273 | } | ||
274 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
275 | snd_es1688_read(chip, 0xb1); | ||
276 | snd_es1688_read(chip, 0xb2); | ||
277 | snd_es1688_reset(chip); | ||
278 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | |||
284 | */ | ||
285 | |||
286 | static ratnum_t clocks[2] = { | ||
287 | { | ||
288 | .num = 795444, | ||
289 | .den_min = 1, | ||
290 | .den_max = 128, | ||
291 | .den_step = 1, | ||
292 | }, | ||
293 | { | ||
294 | .num = 397722, | ||
295 | .den_min = 1, | ||
296 | .den_max = 128, | ||
297 | .den_step = 1, | ||
298 | } | ||
299 | }; | ||
300 | |||
301 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { | ||
302 | .nrats = 2, | ||
303 | .rats = clocks, | ||
304 | }; | ||
305 | |||
306 | static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream) | ||
307 | { | ||
308 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
309 | unsigned int bits, divider; | ||
310 | |||
311 | if (runtime->rate_num == clocks[0].num) | ||
312 | bits = 256 - runtime->rate_den; | ||
313 | else | ||
314 | bits = 128 - runtime->rate_den; | ||
315 | /* set filter register */ | ||
316 | divider = 256 - 7160000*20/(8*82*runtime->rate); | ||
317 | /* write result to hardware */ | ||
318 | snd_es1688_write(chip, 0xa1, bits); | ||
319 | snd_es1688_write(chip, 0xa2, divider); | ||
320 | } | ||
321 | |||
322 | static int snd_es1688_ioctl(snd_pcm_substream_t * substream, | ||
323 | unsigned int cmd, void *arg) | ||
324 | { | ||
325 | return snd_pcm_lib_ioctl(substream, cmd, arg); | ||
326 | } | ||
327 | |||
328 | static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value) | ||
329 | { | ||
330 | int val; | ||
331 | |||
332 | if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
333 | value = 0x00; | ||
334 | } else if (cmd != SNDRV_PCM_TRIGGER_START) { | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | spin_lock(&chip->reg_lock); | ||
338 | chip->trigger_value = value; | ||
339 | val = snd_es1688_read(chip, 0xb8); | ||
340 | if ((val < 0) || (val & 0x0f) == value) { | ||
341 | spin_unlock(&chip->reg_lock); | ||
342 | return -EINVAL; /* something is wrong */ | ||
343 | } | ||
344 | #if 0 | ||
345 | printk("trigger: val = 0x%x, value = 0x%x\n", val, value); | ||
346 | printk("trigger: pointer = 0x%x\n", snd_dma_pointer(chip->dma8, chip->dma_size)); | ||
347 | #endif | ||
348 | snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); | ||
349 | spin_unlock(&chip->reg_lock); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int snd_es1688_hw_params(snd_pcm_substream_t * substream, | ||
354 | snd_pcm_hw_params_t * hw_params) | ||
355 | { | ||
356 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
357 | } | ||
358 | |||
359 | static int snd_es1688_hw_free(snd_pcm_substream_t * substream) | ||
360 | { | ||
361 | return snd_pcm_lib_free_pages(substream); | ||
362 | } | ||
363 | |||
364 | static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream) | ||
365 | { | ||
366 | unsigned long flags; | ||
367 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
368 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
369 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
370 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
371 | |||
372 | chip->dma_size = size; | ||
373 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
374 | snd_es1688_reset(chip); | ||
375 | snd_es1688_set_rate(chip, substream); | ||
376 | snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ | ||
377 | snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); | ||
378 | snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ | ||
379 | if (runtime->channels == 1) { | ||
380 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
381 | /* 8. bit mono */ | ||
382 | snd_es1688_write(chip, 0xb6, 0x80); | ||
383 | snd_es1688_write(chip, 0xb7, 0x51); | ||
384 | snd_es1688_write(chip, 0xb7, 0xd0); | ||
385 | } else { | ||
386 | /* 16. bit mono */ | ||
387 | snd_es1688_write(chip, 0xb6, 0x00); | ||
388 | snd_es1688_write(chip, 0xb7, 0x71); | ||
389 | snd_es1688_write(chip, 0xb7, 0xf4); | ||
390 | } | ||
391 | } else { | ||
392 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
393 | /* 8. bit stereo */ | ||
394 | snd_es1688_write(chip, 0xb6, 0x80); | ||
395 | snd_es1688_write(chip, 0xb7, 0x51); | ||
396 | snd_es1688_write(chip, 0xb7, 0x98); | ||
397 | } else { | ||
398 | /* 16. bit stereo */ | ||
399 | snd_es1688_write(chip, 0xb6, 0x00); | ||
400 | snd_es1688_write(chip, 0xb7, 0x71); | ||
401 | snd_es1688_write(chip, 0xb7, 0xbc); | ||
402 | } | ||
403 | } | ||
404 | snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); | ||
405 | snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); | ||
406 | snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); | ||
407 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
408 | /* --- */ | ||
409 | count = -count; | ||
410 | snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
411 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
412 | snd_es1688_write(chip, 0xa4, (unsigned char) count); | ||
413 | snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); | ||
414 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream, | ||
419 | int cmd) | ||
420 | { | ||
421 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
422 | return snd_es1688_trigger(chip, cmd, 0x05); | ||
423 | } | ||
424 | |||
425 | static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream) | ||
426 | { | ||
427 | unsigned long flags; | ||
428 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
429 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
430 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
431 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
432 | |||
433 | chip->dma_size = size; | ||
434 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
435 | snd_es1688_reset(chip); | ||
436 | snd_es1688_set_rate(chip, substream); | ||
437 | snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); | ||
438 | snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ | ||
439 | snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); | ||
440 | snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ | ||
441 | if (runtime->channels == 1) { | ||
442 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
443 | /* 8. bit mono */ | ||
444 | snd_es1688_write(chip, 0xb7, 0x51); | ||
445 | snd_es1688_write(chip, 0xb7, 0xd0); | ||
446 | } else { | ||
447 | /* 16. bit mono */ | ||
448 | snd_es1688_write(chip, 0xb7, 0x71); | ||
449 | snd_es1688_write(chip, 0xb7, 0xf4); | ||
450 | } | ||
451 | } else { | ||
452 | if (snd_pcm_format_width(runtime->format) == 8) { | ||
453 | /* 8. bit stereo */ | ||
454 | snd_es1688_write(chip, 0xb7, 0x51); | ||
455 | snd_es1688_write(chip, 0xb7, 0x98); | ||
456 | } else { | ||
457 | /* 16. bit stereo */ | ||
458 | snd_es1688_write(chip, 0xb7, 0x71); | ||
459 | snd_es1688_write(chip, 0xb7, 0xbc); | ||
460 | } | ||
461 | } | ||
462 | snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); | ||
463 | snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); | ||
464 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
465 | /* --- */ | ||
466 | count = -count; | ||
467 | snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | ||
468 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
469 | snd_es1688_write(chip, 0xa4, (unsigned char) count); | ||
470 | snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); | ||
471 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream, | ||
476 | int cmd) | ||
477 | { | ||
478 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
479 | return snd_es1688_trigger(chip, cmd, 0x0f); | ||
480 | } | ||
481 | |||
482 | static irqreturn_t snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
483 | { | ||
484 | es1688_t *chip = dev_id; | ||
485 | |||
486 | if (chip->trigger_value == 0x05) /* ok.. playback is active */ | ||
487 | snd_pcm_period_elapsed(chip->playback_substream); | ||
488 | if (chip->trigger_value == 0x0f) /* ok.. capture is active */ | ||
489 | snd_pcm_period_elapsed(chip->capture_substream); | ||
490 | |||
491 | inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ | ||
492 | return IRQ_HANDLED; | ||
493 | } | ||
494 | |||
495 | static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream) | ||
496 | { | ||
497 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
498 | size_t ptr; | ||
499 | |||
500 | if (chip->trigger_value != 0x05) | ||
501 | return 0; | ||
502 | ptr = snd_dma_pointer(chip->dma8, chip->dma_size); | ||
503 | return bytes_to_frames(substream->runtime, ptr); | ||
504 | } | ||
505 | |||
506 | static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream) | ||
507 | { | ||
508 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
509 | size_t ptr; | ||
510 | |||
511 | if (chip->trigger_value != 0x0f) | ||
512 | return 0; | ||
513 | ptr = snd_dma_pointer(chip->dma8, chip->dma_size); | ||
514 | return bytes_to_frames(substream->runtime, ptr); | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | |||
519 | */ | ||
520 | |||
521 | static snd_pcm_hardware_t snd_es1688_playback = | ||
522 | { | ||
523 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
524 | SNDRV_PCM_INFO_MMAP_VALID), | ||
525 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
526 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
527 | .rate_min = 4000, | ||
528 | .rate_max = 48000, | ||
529 | .channels_min = 1, | ||
530 | .channels_max = 2, | ||
531 | .buffer_bytes_max = 65536, | ||
532 | .period_bytes_min = 64, | ||
533 | .period_bytes_max = 65536, | ||
534 | .periods_min = 1, | ||
535 | .periods_max = 1024, | ||
536 | .fifo_size = 0, | ||
537 | }; | ||
538 | |||
539 | static snd_pcm_hardware_t snd_es1688_capture = | ||
540 | { | ||
541 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
542 | SNDRV_PCM_INFO_MMAP_VALID), | ||
543 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
544 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
545 | .rate_min = 4000, | ||
546 | .rate_max = 48000, | ||
547 | .channels_min = 1, | ||
548 | .channels_max = 2, | ||
549 | .buffer_bytes_max = 65536, | ||
550 | .period_bytes_min = 64, | ||
551 | .period_bytes_max = 65536, | ||
552 | .periods_min = 1, | ||
553 | .periods_max = 1024, | ||
554 | .fifo_size = 0, | ||
555 | }; | ||
556 | |||
557 | /* | ||
558 | |||
559 | */ | ||
560 | |||
561 | static int snd_es1688_playback_open(snd_pcm_substream_t * substream) | ||
562 | { | ||
563 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
564 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
565 | |||
566 | if (chip->capture_substream != NULL) | ||
567 | return -EAGAIN; | ||
568 | chip->playback_substream = substream; | ||
569 | runtime->hw = snd_es1688_playback; | ||
570 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
571 | &hw_constraints_clocks); | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static int snd_es1688_capture_open(snd_pcm_substream_t * substream) | ||
576 | { | ||
577 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
578 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
579 | |||
580 | if (chip->playback_substream != NULL) | ||
581 | return -EAGAIN; | ||
582 | chip->capture_substream = substream; | ||
583 | runtime->hw = snd_es1688_capture; | ||
584 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
585 | &hw_constraints_clocks); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static int snd_es1688_playback_close(snd_pcm_substream_t * substream) | ||
590 | { | ||
591 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
592 | |||
593 | chip->playback_substream = NULL; | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static int snd_es1688_capture_close(snd_pcm_substream_t * substream) | ||
598 | { | ||
599 | es1688_t *chip = snd_pcm_substream_chip(substream); | ||
600 | |||
601 | chip->capture_substream = NULL; | ||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int snd_es1688_free(es1688_t *chip) | ||
606 | { | ||
607 | if (chip->res_port) { | ||
608 | snd_es1688_init(chip, 0); | ||
609 | release_resource(chip->res_port); | ||
610 | kfree_nocheck(chip->res_port); | ||
611 | } | ||
612 | if (chip->irq >= 0) | ||
613 | free_irq(chip->irq, (void *) chip); | ||
614 | if (chip->dma8 >= 0) { | ||
615 | disable_dma(chip->dma8); | ||
616 | free_dma(chip->dma8); | ||
617 | } | ||
618 | kfree(chip); | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int snd_es1688_dev_free(snd_device_t *device) | ||
623 | { | ||
624 | es1688_t *chip = device->device_data; | ||
625 | return snd_es1688_free(chip); | ||
626 | } | ||
627 | |||
628 | static const char *snd_es1688_chip_id(es1688_t *chip) | ||
629 | { | ||
630 | static char tmp[16]; | ||
631 | sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); | ||
632 | return tmp; | ||
633 | } | ||
634 | |||
635 | int snd_es1688_create(snd_card_t * card, | ||
636 | unsigned long port, | ||
637 | unsigned long mpu_port, | ||
638 | int irq, | ||
639 | int mpu_irq, | ||
640 | int dma8, | ||
641 | unsigned short hardware, | ||
642 | es1688_t **rchip) | ||
643 | { | ||
644 | static snd_device_ops_t ops = { | ||
645 | .dev_free = snd_es1688_dev_free, | ||
646 | }; | ||
647 | |||
648 | es1688_t *chip; | ||
649 | int err; | ||
650 | |||
651 | *rchip = NULL; | ||
652 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
653 | if (chip == NULL) | ||
654 | return -ENOMEM; | ||
655 | chip->irq = -1; | ||
656 | chip->dma8 = -1; | ||
657 | |||
658 | if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) { | ||
659 | snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4); | ||
660 | snd_es1688_free(chip); | ||
661 | return -EBUSY; | ||
662 | } | ||
663 | if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) { | ||
664 | snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq); | ||
665 | snd_es1688_free(chip); | ||
666 | return -EBUSY; | ||
667 | } | ||
668 | chip->irq = irq; | ||
669 | if (request_dma(dma8, "ES1688")) { | ||
670 | snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8); | ||
671 | snd_es1688_free(chip); | ||
672 | return -EBUSY; | ||
673 | } | ||
674 | chip->dma8 = dma8; | ||
675 | |||
676 | spin_lock_init(&chip->reg_lock); | ||
677 | spin_lock_init(&chip->mixer_lock); | ||
678 | chip->card = card; | ||
679 | chip->port = port; | ||
680 | mpu_port &= ~0x000f; | ||
681 | if (mpu_port < 0x300 || mpu_port > 0x330) | ||
682 | mpu_port = 0; | ||
683 | chip->mpu_port = mpu_port; | ||
684 | chip->mpu_irq = mpu_irq; | ||
685 | chip->hardware = hardware; | ||
686 | |||
687 | if ((err = snd_es1688_probe(chip)) < 0) { | ||
688 | snd_es1688_free(chip); | ||
689 | return err; | ||
690 | } | ||
691 | if ((err = snd_es1688_init(chip, 1)) < 0) { | ||
692 | snd_es1688_free(chip); | ||
693 | return err; | ||
694 | } | ||
695 | |||
696 | /* Register device */ | ||
697 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
698 | snd_es1688_free(chip); | ||
699 | return err; | ||
700 | } | ||
701 | |||
702 | *rchip = chip; | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static snd_pcm_ops_t snd_es1688_playback_ops = { | ||
707 | .open = snd_es1688_playback_open, | ||
708 | .close = snd_es1688_playback_close, | ||
709 | .ioctl = snd_es1688_ioctl, | ||
710 | .hw_params = snd_es1688_hw_params, | ||
711 | .hw_free = snd_es1688_hw_free, | ||
712 | .prepare = snd_es1688_playback_prepare, | ||
713 | .trigger = snd_es1688_playback_trigger, | ||
714 | .pointer = snd_es1688_playback_pointer, | ||
715 | }; | ||
716 | |||
717 | static snd_pcm_ops_t snd_es1688_capture_ops = { | ||
718 | .open = snd_es1688_capture_open, | ||
719 | .close = snd_es1688_capture_close, | ||
720 | .ioctl = snd_es1688_ioctl, | ||
721 | .hw_params = snd_es1688_hw_params, | ||
722 | .hw_free = snd_es1688_hw_free, | ||
723 | .prepare = snd_es1688_capture_prepare, | ||
724 | .trigger = snd_es1688_capture_trigger, | ||
725 | .pointer = snd_es1688_capture_pointer, | ||
726 | }; | ||
727 | |||
728 | static void snd_es1688_pcm_free(snd_pcm_t *pcm) | ||
729 | { | ||
730 | es1688_t *chip = pcm->private_data; | ||
731 | chip->pcm = NULL; | ||
732 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
733 | } | ||
734 | |||
735 | int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm) | ||
736 | { | ||
737 | snd_pcm_t *pcm; | ||
738 | int err; | ||
739 | |||
740 | if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0) | ||
741 | return err; | ||
742 | |||
743 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); | ||
744 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); | ||
745 | |||
746 | pcm->private_data = chip; | ||
747 | pcm->private_free = snd_es1688_pcm_free; | ||
748 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
749 | sprintf(pcm->name, snd_es1688_chip_id(chip)); | ||
750 | chip->pcm = pcm; | ||
751 | |||
752 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
753 | snd_dma_isa_data(), | ||
754 | 64*1024, 64*1024); | ||
755 | |||
756 | if (rpcm) | ||
757 | *rpcm = pcm; | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | /* | ||
762 | * MIXER part | ||
763 | */ | ||
764 | |||
765 | static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
766 | { | ||
767 | static char *texts[9] = { | ||
768 | "Mic", "Mic Master", "CD", "AOUT", | ||
769 | "Mic1", "Mix", "Line", "Master" | ||
770 | }; | ||
771 | |||
772 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
773 | uinfo->count = 1; | ||
774 | uinfo->value.enumerated.items = 8; | ||
775 | if (uinfo->value.enumerated.item > 7) | ||
776 | uinfo->value.enumerated.item = 7; | ||
777 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
782 | { | ||
783 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
784 | ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; | ||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
789 | { | ||
790 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
791 | unsigned long flags; | ||
792 | unsigned char oval, nval; | ||
793 | int change; | ||
794 | |||
795 | if (ucontrol->value.enumerated.item[0] > 8) | ||
796 | return -EINVAL; | ||
797 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
798 | oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); | ||
799 | nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); | ||
800 | change = nval != oval; | ||
801 | if (change) | ||
802 | snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); | ||
803 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
804 | return change; | ||
805 | } | ||
806 | |||
807 | #define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
808 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
809 | .info = snd_es1688_info_single, \ | ||
810 | .get = snd_es1688_get_single, .put = snd_es1688_put_single, \ | ||
811 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
812 | |||
813 | static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
814 | { | ||
815 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
816 | |||
817 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
818 | uinfo->count = 1; | ||
819 | uinfo->value.integer.min = 0; | ||
820 | uinfo->value.integer.max = mask; | ||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
825 | { | ||
826 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
827 | unsigned long flags; | ||
828 | int reg = kcontrol->private_value & 0xff; | ||
829 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
830 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
831 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
832 | |||
833 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
834 | ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; | ||
835 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
836 | if (invert) | ||
837 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
838 | return 0; | ||
839 | } | ||
840 | |||
841 | static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
842 | { | ||
843 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
844 | unsigned long flags; | ||
845 | int reg = kcontrol->private_value & 0xff; | ||
846 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
847 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
848 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
849 | int change; | ||
850 | unsigned char oval, nval; | ||
851 | |||
852 | nval = (ucontrol->value.integer.value[0] & mask); | ||
853 | if (invert) | ||
854 | nval = mask - nval; | ||
855 | nval <<= shift; | ||
856 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
857 | oval = snd_es1688_mixer_read(chip, reg); | ||
858 | nval = (oval & ~(mask << shift)) | nval; | ||
859 | change = nval != oval; | ||
860 | if (change) | ||
861 | snd_es1688_mixer_write(chip, reg, nval); | ||
862 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
863 | return change; | ||
864 | } | ||
865 | |||
866 | #define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
867 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
868 | .info = snd_es1688_info_double, \ | ||
869 | .get = snd_es1688_get_double, .put = snd_es1688_put_double, \ | ||
870 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
871 | |||
872 | static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
873 | { | ||
874 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
875 | |||
876 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
877 | uinfo->count = 2; | ||
878 | uinfo->value.integer.min = 0; | ||
879 | uinfo->value.integer.max = mask; | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
884 | { | ||
885 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
886 | unsigned long flags; | ||
887 | int left_reg = kcontrol->private_value & 0xff; | ||
888 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
889 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
890 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
891 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
892 | int invert = (kcontrol->private_value >> 22) & 1; | ||
893 | unsigned char left, right; | ||
894 | |||
895 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
896 | if (left_reg < 0xa0) | ||
897 | left = snd_es1688_mixer_read(chip, left_reg); | ||
898 | else | ||
899 | left = snd_es1688_read(chip, left_reg); | ||
900 | if (left_reg != right_reg) { | ||
901 | if (right_reg < 0xa0) | ||
902 | right = snd_es1688_mixer_read(chip, right_reg); | ||
903 | else | ||
904 | right = snd_es1688_read(chip, right_reg); | ||
905 | } else | ||
906 | right = left; | ||
907 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
908 | ucontrol->value.integer.value[0] = (left >> shift_left) & mask; | ||
909 | ucontrol->value.integer.value[1] = (right >> shift_right) & mask; | ||
910 | if (invert) { | ||
911 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
912 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
913 | } | ||
914 | return 0; | ||
915 | } | ||
916 | |||
917 | static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
918 | { | ||
919 | es1688_t *chip = snd_kcontrol_chip(kcontrol); | ||
920 | unsigned long flags; | ||
921 | int left_reg = kcontrol->private_value & 0xff; | ||
922 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
923 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
924 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
925 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
926 | int invert = (kcontrol->private_value >> 22) & 1; | ||
927 | int change; | ||
928 | unsigned char val1, val2, oval1, oval2; | ||
929 | |||
930 | val1 = ucontrol->value.integer.value[0] & mask; | ||
931 | val2 = ucontrol->value.integer.value[1] & mask; | ||
932 | if (invert) { | ||
933 | val1 = mask - val1; | ||
934 | val2 = mask - val2; | ||
935 | } | ||
936 | val1 <<= shift_left; | ||
937 | val2 <<= shift_right; | ||
938 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
939 | if (left_reg != right_reg) { | ||
940 | if (left_reg < 0xa0) | ||
941 | oval1 = snd_es1688_mixer_read(chip, left_reg); | ||
942 | else | ||
943 | oval1 = snd_es1688_read(chip, left_reg); | ||
944 | if (right_reg < 0xa0) | ||
945 | oval2 = snd_es1688_mixer_read(chip, right_reg); | ||
946 | else | ||
947 | oval2 = snd_es1688_read(chip, right_reg); | ||
948 | val1 = (oval1 & ~(mask << shift_left)) | val1; | ||
949 | val2 = (oval2 & ~(mask << shift_right)) | val2; | ||
950 | change = val1 != oval1 || val2 != oval2; | ||
951 | if (change) { | ||
952 | if (left_reg < 0xa0) | ||
953 | snd_es1688_mixer_write(chip, left_reg, val1); | ||
954 | else | ||
955 | snd_es1688_write(chip, left_reg, val1); | ||
956 | if (right_reg < 0xa0) | ||
957 | snd_es1688_mixer_write(chip, right_reg, val1); | ||
958 | else | ||
959 | snd_es1688_write(chip, right_reg, val1); | ||
960 | } | ||
961 | } else { | ||
962 | if (left_reg < 0xa0) | ||
963 | oval1 = snd_es1688_mixer_read(chip, left_reg); | ||
964 | else | ||
965 | oval1 = snd_es1688_read(chip, left_reg); | ||
966 | val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
967 | change = val1 != oval1; | ||
968 | if (change) { | ||
969 | if (left_reg < 0xa0) | ||
970 | snd_es1688_mixer_write(chip, left_reg, val1); | ||
971 | else | ||
972 | snd_es1688_write(chip, left_reg, val1); | ||
973 | } | ||
974 | |||
975 | } | ||
976 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
977 | return change; | ||
978 | } | ||
979 | |||
980 | static snd_kcontrol_new_t snd_es1688_controls[] = { | ||
981 | ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), | ||
982 | ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), | ||
983 | ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), | ||
984 | ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), | ||
985 | ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), | ||
986 | ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), | ||
987 | ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), | ||
988 | ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), | ||
989 | ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), | ||
990 | ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), | ||
991 | { | ||
992 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
993 | .name = "Capture Source", | ||
994 | .info = snd_es1688_info_mux, | ||
995 | .get = snd_es1688_get_mux, | ||
996 | .put = snd_es1688_put_mux, | ||
997 | }, | ||
998 | }; | ||
999 | |||
1000 | #define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) | ||
1001 | |||
1002 | static unsigned char snd_es1688_init_table[][2] = { | ||
1003 | { ES1688_MASTER_DEV, 0 }, | ||
1004 | { ES1688_PCM_DEV, 0 }, | ||
1005 | { ES1688_LINE_DEV, 0 }, | ||
1006 | { ES1688_CD_DEV, 0 }, | ||
1007 | { ES1688_FM_DEV, 0 }, | ||
1008 | { ES1688_MIC_DEV, 0 }, | ||
1009 | { ES1688_AUX_DEV, 0 }, | ||
1010 | { ES1688_SPEAKER_DEV, 0 }, | ||
1011 | { ES1688_RECLEV_DEV, 0 }, | ||
1012 | { ES1688_REC_DEV, 0x17 } | ||
1013 | }; | ||
1014 | |||
1015 | int snd_es1688_mixer(es1688_t *chip) | ||
1016 | { | ||
1017 | snd_card_t *card; | ||
1018 | unsigned int idx; | ||
1019 | int err; | ||
1020 | unsigned char reg, val; | ||
1021 | |||
1022 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
1023 | |||
1024 | card = chip->card; | ||
1025 | |||
1026 | strcpy(card->mixername, snd_es1688_chip_id(chip)); | ||
1027 | |||
1028 | for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { | ||
1029 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) | ||
1030 | return err; | ||
1031 | } | ||
1032 | for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { | ||
1033 | reg = snd_es1688_init_table[idx][0]; | ||
1034 | val = snd_es1688_init_table[idx][1]; | ||
1035 | if (reg < 0xa0) | ||
1036 | snd_es1688_mixer_write(chip, reg, val); | ||
1037 | else | ||
1038 | snd_es1688_write(chip, reg, val); | ||
1039 | } | ||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | EXPORT_SYMBOL(snd_es1688_mixer_write); | ||
1044 | EXPORT_SYMBOL(snd_es1688_create); | ||
1045 | EXPORT_SYMBOL(snd_es1688_pcm); | ||
1046 | EXPORT_SYMBOL(snd_es1688_mixer); | ||
1047 | |||
1048 | /* | ||
1049 | * INIT part | ||
1050 | */ | ||
1051 | |||
1052 | static int __init alsa_es1688_init(void) | ||
1053 | { | ||
1054 | return 0; | ||
1055 | } | ||
1056 | |||
1057 | static void __exit alsa_es1688_exit(void) | ||
1058 | { | ||
1059 | } | ||
1060 | |||
1061 | module_init(alsa_es1688_init) | ||
1062 | module_exit(alsa_es1688_exit) | ||
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c new file mode 100644 index 000000000000..1d832b2adb7c --- /dev/null +++ b/sound/isa/es18xx.c | |||
@@ -0,0 +1,2224 @@ | |||
1 | /* | ||
2 | * Driver for generic ESS AudioDrive ES18xx soundcards | ||
3 | * Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de> | ||
4 | * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org> | ||
5 | * | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | /* GENERAL NOTES: | ||
23 | * | ||
24 | * BUGS: | ||
25 | * - There are pops (we can't delay in trigger function, cause midlevel | ||
26 | * often need to trigger down and then up very quickly). | ||
27 | * Any ideas? | ||
28 | * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. | ||
29 | */ | ||
30 | |||
31 | /* | ||
32 | * ES1868 NOTES: | ||
33 | * - The chip has one half duplex pcm (with very limited full duplex support). | ||
34 | * | ||
35 | * - Duplex stereophonic sound is impossible. | ||
36 | * - Record and playback must share the same frequency rate. | ||
37 | * | ||
38 | * - The driver use dma2 for playback and dma1 for capture. | ||
39 | */ | ||
40 | |||
41 | /* | ||
42 | * ES1869 NOTES: | ||
43 | * | ||
44 | * - there are a first full duplex pcm and a second playback only pcm | ||
45 | * (incompatible with first pcm capture) | ||
46 | * | ||
47 | * - there is support for the capture volume and ESS Spatializer 3D effect. | ||
48 | * | ||
49 | * - contrarily to some pages in DS_1869.PDF the rates can be set | ||
50 | * independently. | ||
51 | * | ||
52 | * BUGS: | ||
53 | * | ||
54 | * - There is a major trouble I noted: | ||
55 | * | ||
56 | * using both channel for playback stereo 16 bit samples at 44100 Hz | ||
57 | * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. | ||
58 | * | ||
59 | * The same happens using Audio1 for captureing. | ||
60 | * | ||
61 | * The Windows driver does not suffer of this (although it use Audio1 | ||
62 | * only for captureing). I'm unable to discover why. | ||
63 | * | ||
64 | */ | ||
65 | |||
66 | |||
67 | #include <sound/driver.h> | ||
68 | #include <asm/io.h> | ||
69 | #include <asm/dma.h> | ||
70 | #include <linux/init.h> | ||
71 | #include <linux/pm.h> | ||
72 | #include <linux/slab.h> | ||
73 | #include <linux/pnp.h> | ||
74 | #include <linux/isapnp.h> | ||
75 | #include <linux/moduleparam.h> | ||
76 | #include <sound/core.h> | ||
77 | #include <sound/control.h> | ||
78 | #include <sound/pcm.h> | ||
79 | #include <sound/pcm_params.h> | ||
80 | #include <sound/mpu401.h> | ||
81 | #include <sound/opl3.h> | ||
82 | #define SNDRV_LEGACY_AUTO_PROBE | ||
83 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
84 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
85 | #include <sound/initval.h> | ||
86 | |||
87 | #define PFX "es18xx: " | ||
88 | |||
89 | struct _snd_es18xx { | ||
90 | unsigned long port; /* port of ESS chip */ | ||
91 | unsigned long mpu_port; /* MPU-401 port of ESS chip */ | ||
92 | unsigned long fm_port; /* FM port */ | ||
93 | unsigned long ctrl_port; /* Control port of ESS chip */ | ||
94 | struct resource *res_port; | ||
95 | struct resource *res_mpu_port; | ||
96 | struct resource *res_ctrl_port; | ||
97 | int irq; /* IRQ number of ESS chip */ | ||
98 | int dma1; /* DMA1 */ | ||
99 | int dma2; /* DMA2 */ | ||
100 | unsigned short version; /* version of ESS chip */ | ||
101 | int caps; /* Chip capabilities */ | ||
102 | unsigned short audio2_vol; /* volume level of audio2 */ | ||
103 | |||
104 | unsigned short active; /* active channel mask */ | ||
105 | unsigned int dma1_size; | ||
106 | unsigned int dma2_size; | ||
107 | unsigned int dma1_shift; | ||
108 | unsigned int dma2_shift; | ||
109 | |||
110 | snd_card_t *card; | ||
111 | snd_pcm_t *pcm; | ||
112 | snd_pcm_substream_t *playback_a_substream; | ||
113 | snd_pcm_substream_t *capture_a_substream; | ||
114 | snd_pcm_substream_t *playback_b_substream; | ||
115 | |||
116 | snd_rawmidi_t *rmidi; | ||
117 | |||
118 | snd_kcontrol_t *hw_volume; | ||
119 | snd_kcontrol_t *hw_switch; | ||
120 | snd_kcontrol_t *master_volume; | ||
121 | snd_kcontrol_t *master_switch; | ||
122 | |||
123 | spinlock_t reg_lock; | ||
124 | spinlock_t mixer_lock; | ||
125 | spinlock_t ctrl_lock; | ||
126 | #ifdef CONFIG_PM | ||
127 | unsigned char pm_reg; | ||
128 | #endif | ||
129 | }; | ||
130 | |||
131 | #define AUDIO1_IRQ 0x01 | ||
132 | #define AUDIO2_IRQ 0x02 | ||
133 | #define HWV_IRQ 0x04 | ||
134 | #define MPU_IRQ 0x08 | ||
135 | |||
136 | #define ES18XX_PCM2 0x0001 /* Has two useable PCM */ | ||
137 | #define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */ | ||
138 | #define ES18XX_RECMIX 0x0004 /* Has record mixer */ | ||
139 | #define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */ | ||
140 | #define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ | ||
141 | #define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ | ||
142 | #define ES18XX_AUXB 0x0040 /* AuxB mixer control */ | ||
143 | #define ES18XX_HWV 0x0080 /* Has hardware volume */ | ||
144 | #define ES18XX_MONO 0x0100 /* Mono_in mixer control */ | ||
145 | #define ES18XX_I2S 0x0200 /* I2S mixer control */ | ||
146 | #define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ | ||
147 | #define ES18XX_CONTROL 0x0800 /* Has control ports */ | ||
148 | |||
149 | /* Power Management */ | ||
150 | #define ES18XX_PM 0x07 | ||
151 | #define ES18XX_PM_GPO0 0x01 | ||
152 | #define ES18XX_PM_GPO1 0x02 | ||
153 | #define ES18XX_PM_PDR 0x04 | ||
154 | #define ES18XX_PM_ANA 0x08 | ||
155 | #define ES18XX_PM_FM 0x020 | ||
156 | #define ES18XX_PM_SUS 0x080 | ||
157 | |||
158 | typedef struct _snd_es18xx es18xx_t; | ||
159 | |||
160 | /* Lowlevel */ | ||
161 | |||
162 | #define DAC1 0x01 | ||
163 | #define ADC1 0x02 | ||
164 | #define DAC2 0x04 | ||
165 | #define MILLISECOND 10000 | ||
166 | |||
167 | static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val) | ||
168 | { | ||
169 | int i; | ||
170 | |||
171 | for(i = MILLISECOND; i; i--) | ||
172 | if ((inb(chip->port + 0x0C) & 0x80) == 0) { | ||
173 | outb(val, chip->port + 0x0C); | ||
174 | return 0; | ||
175 | } | ||
176 | snd_printk("dsp_command: timeout (0x%x)\n", val); | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | |||
180 | static int snd_es18xx_dsp_get_byte(es18xx_t *chip) | ||
181 | { | ||
182 | int i; | ||
183 | |||
184 | for(i = MILLISECOND/10; i; i--) | ||
185 | if (inb(chip->port + 0x0C) & 0x40) | ||
186 | return inb(chip->port + 0x0A); | ||
187 | snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A)); | ||
188 | return -ENODEV; | ||
189 | } | ||
190 | |||
191 | #undef REG_DEBUG | ||
192 | |||
193 | static int snd_es18xx_write(es18xx_t *chip, | ||
194 | unsigned char reg, unsigned char data) | ||
195 | { | ||
196 | unsigned long flags; | ||
197 | int ret; | ||
198 | |||
199 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
200 | ret = snd_es18xx_dsp_command(chip, reg); | ||
201 | if (ret < 0) | ||
202 | goto end; | ||
203 | ret = snd_es18xx_dsp_command(chip, data); | ||
204 | end: | ||
205 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
206 | #ifdef REG_DEBUG | ||
207 | snd_printk("Reg %02x set to %02x\n", reg, data); | ||
208 | #endif | ||
209 | return ret; | ||
210 | } | ||
211 | |||
212 | static int snd_es18xx_read(es18xx_t *chip, unsigned char reg) | ||
213 | { | ||
214 | unsigned long flags; | ||
215 | int ret, data; | ||
216 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
217 | ret = snd_es18xx_dsp_command(chip, 0xC0); | ||
218 | if (ret < 0) | ||
219 | goto end; | ||
220 | ret = snd_es18xx_dsp_command(chip, reg); | ||
221 | if (ret < 0) | ||
222 | goto end; | ||
223 | data = snd_es18xx_dsp_get_byte(chip); | ||
224 | ret = data; | ||
225 | #ifdef REG_DEBUG | ||
226 | snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret); | ||
227 | #endif | ||
228 | end: | ||
229 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | /* Return old value */ | ||
234 | static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg, | ||
235 | unsigned char mask, unsigned char val) | ||
236 | { | ||
237 | int ret; | ||
238 | unsigned char old, new, oval; | ||
239 | unsigned long flags; | ||
240 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
241 | ret = snd_es18xx_dsp_command(chip, 0xC0); | ||
242 | if (ret < 0) | ||
243 | goto end; | ||
244 | ret = snd_es18xx_dsp_command(chip, reg); | ||
245 | if (ret < 0) | ||
246 | goto end; | ||
247 | ret = snd_es18xx_dsp_get_byte(chip); | ||
248 | if (ret < 0) { | ||
249 | goto end; | ||
250 | } | ||
251 | old = ret; | ||
252 | oval = old & mask; | ||
253 | if (val != oval) { | ||
254 | ret = snd_es18xx_dsp_command(chip, reg); | ||
255 | if (ret < 0) | ||
256 | goto end; | ||
257 | new = (old & ~mask) | (val & mask); | ||
258 | ret = snd_es18xx_dsp_command(chip, new); | ||
259 | if (ret < 0) | ||
260 | goto end; | ||
261 | #ifdef REG_DEBUG | ||
262 | snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret); | ||
263 | #endif | ||
264 | } | ||
265 | ret = oval; | ||
266 | end: | ||
267 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
268 | return ret; | ||
269 | } | ||
270 | |||
271 | static inline void snd_es18xx_mixer_write(es18xx_t *chip, | ||
272 | unsigned char reg, unsigned char data) | ||
273 | { | ||
274 | unsigned long flags; | ||
275 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
276 | outb(reg, chip->port + 0x04); | ||
277 | outb(data, chip->port + 0x05); | ||
278 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
279 | #ifdef REG_DEBUG | ||
280 | snd_printk("Mixer reg %02x set to %02x\n", reg, data); | ||
281 | #endif | ||
282 | } | ||
283 | |||
284 | static inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg) | ||
285 | { | ||
286 | unsigned long flags; | ||
287 | int data; | ||
288 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
289 | outb(reg, chip->port + 0x04); | ||
290 | data = inb(chip->port + 0x05); | ||
291 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
292 | #ifdef REG_DEBUG | ||
293 | snd_printk("Mixer reg %02x now is %02x\n", reg, data); | ||
294 | #endif | ||
295 | return data; | ||
296 | } | ||
297 | |||
298 | /* Return old value */ | ||
299 | static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg, | ||
300 | unsigned char mask, unsigned char val) | ||
301 | { | ||
302 | unsigned char old, new, oval; | ||
303 | unsigned long flags; | ||
304 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
305 | outb(reg, chip->port + 0x04); | ||
306 | old = inb(chip->port + 0x05); | ||
307 | oval = old & mask; | ||
308 | if (val != oval) { | ||
309 | new = (old & ~mask) | (val & mask); | ||
310 | outb(new, chip->port + 0x05); | ||
311 | #ifdef REG_DEBUG | ||
312 | snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); | ||
313 | #endif | ||
314 | } | ||
315 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
316 | return oval; | ||
317 | } | ||
318 | |||
319 | static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg, | ||
320 | unsigned char mask) | ||
321 | { | ||
322 | int old, expected, new; | ||
323 | unsigned long flags; | ||
324 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
325 | outb(reg, chip->port + 0x04); | ||
326 | old = inb(chip->port + 0x05); | ||
327 | expected = old ^ mask; | ||
328 | outb(expected, chip->port + 0x05); | ||
329 | new = inb(chip->port + 0x05); | ||
330 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
331 | #ifdef REG_DEBUG | ||
332 | snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new); | ||
333 | #endif | ||
334 | return expected == new; | ||
335 | } | ||
336 | |||
337 | |||
338 | static int snd_es18xx_reset(es18xx_t *chip) | ||
339 | { | ||
340 | int i; | ||
341 | outb(0x03, chip->port + 0x06); | ||
342 | inb(chip->port + 0x06); | ||
343 | outb(0x00, chip->port + 0x06); | ||
344 | for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); | ||
345 | if (inb(chip->port + 0x0A) != 0xAA) | ||
346 | return -1; | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int snd_es18xx_reset_fifo(es18xx_t *chip) | ||
351 | { | ||
352 | outb(0x02, chip->port + 0x06); | ||
353 | inb(chip->port + 0x06); | ||
354 | outb(0x00, chip->port + 0x06); | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static ratnum_t new_clocks[2] = { | ||
359 | { | ||
360 | .num = 793800, | ||
361 | .den_min = 1, | ||
362 | .den_max = 128, | ||
363 | .den_step = 1, | ||
364 | }, | ||
365 | { | ||
366 | .num = 768000, | ||
367 | .den_min = 1, | ||
368 | .den_max = 128, | ||
369 | .den_step = 1, | ||
370 | } | ||
371 | }; | ||
372 | |||
373 | static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = { | ||
374 | .nrats = 2, | ||
375 | .rats = new_clocks, | ||
376 | }; | ||
377 | |||
378 | static ratnum_t old_clocks[2] = { | ||
379 | { | ||
380 | .num = 795444, | ||
381 | .den_min = 1, | ||
382 | .den_max = 128, | ||
383 | .den_step = 1, | ||
384 | }, | ||
385 | { | ||
386 | .num = 397722, | ||
387 | .den_min = 1, | ||
388 | .den_max = 128, | ||
389 | .den_step = 1, | ||
390 | } | ||
391 | }; | ||
392 | |||
393 | static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks = { | ||
394 | .nrats = 2, | ||
395 | .rats = old_clocks, | ||
396 | }; | ||
397 | |||
398 | |||
399 | static void snd_es18xx_rate_set(es18xx_t *chip, | ||
400 | snd_pcm_substream_t *substream, | ||
401 | int mode) | ||
402 | { | ||
403 | unsigned int bits, div0; | ||
404 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
405 | if (chip->caps & ES18XX_NEW_RATE) { | ||
406 | if (runtime->rate_num == new_clocks[0].num) | ||
407 | bits = 128 - runtime->rate_den; | ||
408 | else | ||
409 | bits = 256 - runtime->rate_den; | ||
410 | } else { | ||
411 | if (runtime->rate_num == old_clocks[0].num) | ||
412 | bits = 256 - runtime->rate_den; | ||
413 | else | ||
414 | bits = 128 - runtime->rate_den; | ||
415 | } | ||
416 | |||
417 | /* set filter register */ | ||
418 | div0 = 256 - 7160000*20/(8*82*runtime->rate); | ||
419 | |||
420 | if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { | ||
421 | snd_es18xx_mixer_write(chip, 0x70, bits); | ||
422 | /* | ||
423 | * Comment from kernel oss driver: | ||
424 | * FKS: fascinating: 0x72 doesn't seem to work. | ||
425 | */ | ||
426 | snd_es18xx_write(chip, 0xA2, div0); | ||
427 | snd_es18xx_mixer_write(chip, 0x72, div0); | ||
428 | } else { | ||
429 | snd_es18xx_write(chip, 0xA1, bits); | ||
430 | snd_es18xx_write(chip, 0xA2, div0); | ||
431 | } | ||
432 | } | ||
433 | |||
434 | static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, | ||
435 | snd_pcm_hw_params_t * hw_params) | ||
436 | { | ||
437 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
438 | int shift, err; | ||
439 | |||
440 | shift = 0; | ||
441 | if (params_channels(hw_params) == 2) | ||
442 | shift++; | ||
443 | if (snd_pcm_format_width(params_format(hw_params)) == 16) | ||
444 | shift++; | ||
445 | |||
446 | if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { | ||
447 | if ((chip->caps & ES18XX_DUPLEX_MONO) && | ||
448 | (chip->capture_a_substream) && | ||
449 | params_channels(hw_params) != 1) { | ||
450 | _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
451 | return -EBUSY; | ||
452 | } | ||
453 | chip->dma2_shift = shift; | ||
454 | } else { | ||
455 | chip->dma1_shift = shift; | ||
456 | } | ||
457 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
458 | return err; | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int snd_es18xx_pcm_hw_free(snd_pcm_substream_t * substream) | ||
463 | { | ||
464 | return snd_pcm_lib_free_pages(substream); | ||
465 | } | ||
466 | |||
467 | static int snd_es18xx_playback1_prepare(es18xx_t *chip, | ||
468 | snd_pcm_substream_t *substream) | ||
469 | { | ||
470 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
471 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
472 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
473 | |||
474 | chip->dma2_size = size; | ||
475 | |||
476 | snd_es18xx_rate_set(chip, substream, DAC2); | ||
477 | |||
478 | /* Transfer Count Reload */ | ||
479 | count = 0x10000 - count; | ||
480 | snd_es18xx_mixer_write(chip, 0x74, count & 0xff); | ||
481 | snd_es18xx_mixer_write(chip, 0x76, count >> 8); | ||
482 | |||
483 | /* Set format */ | ||
484 | snd_es18xx_mixer_bits(chip, 0x7A, 0x07, | ||
485 | ((runtime->channels == 1) ? 0x00 : 0x02) | | ||
486 | (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) | | ||
487 | (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04)); | ||
488 | |||
489 | /* Set DMA controller */ | ||
490 | snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int snd_es18xx_playback1_trigger(es18xx_t *chip, | ||
496 | snd_pcm_substream_t * substream, | ||
497 | int cmd) | ||
498 | { | ||
499 | switch (cmd) { | ||
500 | case SNDRV_PCM_TRIGGER_START: | ||
501 | case SNDRV_PCM_TRIGGER_RESUME: | ||
502 | if (chip->active & DAC2) | ||
503 | return 0; | ||
504 | chip->active |= DAC2; | ||
505 | /* Start DMA */ | ||
506 | if (chip->dma2 >= 4) | ||
507 | snd_es18xx_mixer_write(chip, 0x78, 0xb3); | ||
508 | else | ||
509 | snd_es18xx_mixer_write(chip, 0x78, 0x93); | ||
510 | #ifdef AVOID_POPS | ||
511 | /* Avoid pops */ | ||
512 | udelay(100000); | ||
513 | if (chip->caps & ES18XX_PCM2) | ||
514 | /* Restore Audio 2 volume */ | ||
515 | snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol); | ||
516 | else | ||
517 | /* Enable PCM output */ | ||
518 | snd_es18xx_dsp_command(chip, 0xD1); | ||
519 | #endif | ||
520 | break; | ||
521 | case SNDRV_PCM_TRIGGER_STOP: | ||
522 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
523 | if (!(chip->active & DAC2)) | ||
524 | return 0; | ||
525 | chip->active &= ~DAC2; | ||
526 | /* Stop DMA */ | ||
527 | snd_es18xx_mixer_write(chip, 0x78, 0x00); | ||
528 | #ifdef AVOID_POPS | ||
529 | udelay(25000); | ||
530 | if (chip->caps & ES18XX_PCM2) | ||
531 | /* Set Audio 2 volume to 0 */ | ||
532 | snd_es18xx_mixer_write(chip, 0x7C, 0); | ||
533 | else | ||
534 | /* Disable PCM output */ | ||
535 | snd_es18xx_dsp_command(chip, 0xD3); | ||
536 | #endif | ||
537 | break; | ||
538 | default: | ||
539 | return -EINVAL; | ||
540 | } | ||
541 | |||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static int snd_es18xx_capture_hw_params(snd_pcm_substream_t * substream, | ||
546 | snd_pcm_hw_params_t * hw_params) | ||
547 | { | ||
548 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
549 | int shift, err; | ||
550 | |||
551 | shift = 0; | ||
552 | if ((chip->caps & ES18XX_DUPLEX_MONO) && | ||
553 | chip->playback_a_substream && | ||
554 | params_channels(hw_params) != 1) { | ||
555 | _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
556 | return -EBUSY; | ||
557 | } | ||
558 | if (params_channels(hw_params) == 2) | ||
559 | shift++; | ||
560 | if (snd_pcm_format_width(params_format(hw_params)) == 16) | ||
561 | shift++; | ||
562 | chip->dma1_shift = shift; | ||
563 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
564 | return err; | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | static int snd_es18xx_capture_prepare(snd_pcm_substream_t *substream) | ||
569 | { | ||
570 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
571 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
572 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
573 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
574 | |||
575 | chip->dma1_size = size; | ||
576 | |||
577 | snd_es18xx_reset_fifo(chip); | ||
578 | |||
579 | /* Set stereo/mono */ | ||
580 | snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); | ||
581 | |||
582 | snd_es18xx_rate_set(chip, substream, ADC1); | ||
583 | |||
584 | /* Transfer Count Reload */ | ||
585 | count = 0x10000 - count; | ||
586 | snd_es18xx_write(chip, 0xA4, count & 0xff); | ||
587 | snd_es18xx_write(chip, 0xA5, count >> 8); | ||
588 | |||
589 | #ifdef AVOID_POPS | ||
590 | udelay(100000); | ||
591 | #endif | ||
592 | |||
593 | /* Set format */ | ||
594 | snd_es18xx_write(chip, 0xB7, | ||
595 | snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); | ||
596 | snd_es18xx_write(chip, 0xB7, 0x90 | | ||
597 | ((runtime->channels == 1) ? 0x40 : 0x08) | | ||
598 | (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | | ||
599 | (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); | ||
600 | |||
601 | /* Set DMA controler */ | ||
602 | snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | ||
603 | |||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | static int snd_es18xx_capture_trigger(snd_pcm_substream_t *substream, | ||
608 | int cmd) | ||
609 | { | ||
610 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
611 | |||
612 | switch (cmd) { | ||
613 | case SNDRV_PCM_TRIGGER_START: | ||
614 | case SNDRV_PCM_TRIGGER_RESUME: | ||
615 | if (chip->active & ADC1) | ||
616 | return 0; | ||
617 | chip->active |= ADC1; | ||
618 | /* Start DMA */ | ||
619 | snd_es18xx_write(chip, 0xB8, 0x0f); | ||
620 | break; | ||
621 | case SNDRV_PCM_TRIGGER_STOP: | ||
622 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
623 | if (!(chip->active & ADC1)) | ||
624 | return 0; | ||
625 | chip->active &= ~ADC1; | ||
626 | /* Stop DMA */ | ||
627 | snd_es18xx_write(chip, 0xB8, 0x00); | ||
628 | break; | ||
629 | default: | ||
630 | return -EINVAL; | ||
631 | } | ||
632 | |||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | static int snd_es18xx_playback2_prepare(es18xx_t *chip, | ||
637 | snd_pcm_substream_t *substream) | ||
638 | { | ||
639 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
640 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
641 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
642 | |||
643 | chip->dma1_size = size; | ||
644 | |||
645 | snd_es18xx_reset_fifo(chip); | ||
646 | |||
647 | /* Set stereo/mono */ | ||
648 | snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); | ||
649 | |||
650 | snd_es18xx_rate_set(chip, substream, DAC1); | ||
651 | |||
652 | /* Transfer Count Reload */ | ||
653 | count = 0x10000 - count; | ||
654 | snd_es18xx_write(chip, 0xA4, count & 0xff); | ||
655 | snd_es18xx_write(chip, 0xA5, count >> 8); | ||
656 | |||
657 | /* Set format */ | ||
658 | snd_es18xx_write(chip, 0xB6, | ||
659 | snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00); | ||
660 | snd_es18xx_write(chip, 0xB7, | ||
661 | snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); | ||
662 | snd_es18xx_write(chip, 0xB7, 0x90 | | ||
663 | (runtime->channels == 1 ? 0x40 : 0x08) | | ||
664 | (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | | ||
665 | (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); | ||
666 | |||
667 | /* Set DMA controler */ | ||
668 | snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
669 | |||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int snd_es18xx_playback2_trigger(es18xx_t *chip, | ||
674 | snd_pcm_substream_t *substream, | ||
675 | int cmd) | ||
676 | { | ||
677 | switch (cmd) { | ||
678 | case SNDRV_PCM_TRIGGER_START: | ||
679 | case SNDRV_PCM_TRIGGER_RESUME: | ||
680 | if (chip->active & DAC1) | ||
681 | return 0; | ||
682 | chip->active |= DAC1; | ||
683 | /* Start DMA */ | ||
684 | snd_es18xx_write(chip, 0xB8, 0x05); | ||
685 | #ifdef AVOID_POPS | ||
686 | /* Avoid pops */ | ||
687 | udelay(100000); | ||
688 | /* Enable Audio 1 */ | ||
689 | snd_es18xx_dsp_command(chip, 0xD1); | ||
690 | #endif | ||
691 | break; | ||
692 | case SNDRV_PCM_TRIGGER_STOP: | ||
693 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
694 | if (!(chip->active & DAC1)) | ||
695 | return 0; | ||
696 | chip->active &= ~DAC1; | ||
697 | /* Stop DMA */ | ||
698 | snd_es18xx_write(chip, 0xB8, 0x00); | ||
699 | #ifdef AVOID_POPS | ||
700 | /* Avoid pops */ | ||
701 | udelay(25000); | ||
702 | /* Disable Audio 1 */ | ||
703 | snd_es18xx_dsp_command(chip, 0xD3); | ||
704 | #endif | ||
705 | break; | ||
706 | default: | ||
707 | return -EINVAL; | ||
708 | } | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static int snd_es18xx_playback_prepare(snd_pcm_substream_t *substream) | ||
714 | { | ||
715 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
716 | if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) | ||
717 | return snd_es18xx_playback1_prepare(chip, substream); | ||
718 | else | ||
719 | return snd_es18xx_playback2_prepare(chip, substream); | ||
720 | } | ||
721 | |||
722 | static int snd_es18xx_playback_trigger(snd_pcm_substream_t *substream, | ||
723 | int cmd) | ||
724 | { | ||
725 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
726 | if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) | ||
727 | return snd_es18xx_playback1_trigger(chip, substream, cmd); | ||
728 | else | ||
729 | return snd_es18xx_playback2_trigger(chip, substream, cmd); | ||
730 | } | ||
731 | |||
732 | static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
733 | { | ||
734 | es18xx_t *chip = dev_id; | ||
735 | unsigned char status; | ||
736 | |||
737 | if (chip->caps & ES18XX_CONTROL) { | ||
738 | /* Read Interrupt status */ | ||
739 | status = inb(chip->ctrl_port + 6); | ||
740 | } else { | ||
741 | /* Read Interrupt status */ | ||
742 | status = snd_es18xx_mixer_read(chip, 0x7f) >> 4; | ||
743 | } | ||
744 | #if 0 | ||
745 | else { | ||
746 | status = 0; | ||
747 | if (inb(chip->port + 0x0C) & 0x01) | ||
748 | status |= AUDIO1_IRQ; | ||
749 | if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80) | ||
750 | status |= AUDIO2_IRQ; | ||
751 | if ((chip->caps & ES18XX_HWV) && | ||
752 | snd_es18xx_mixer_read(chip, 0x64) & 0x10) | ||
753 | status |= HWV_IRQ; | ||
754 | } | ||
755 | #endif | ||
756 | |||
757 | /* Audio 1 & Audio 2 */ | ||
758 | if (status & AUDIO2_IRQ) { | ||
759 | if (chip->active & DAC2) | ||
760 | snd_pcm_period_elapsed(chip->playback_a_substream); | ||
761 | /* ack interrupt */ | ||
762 | snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00); | ||
763 | } | ||
764 | if (status & AUDIO1_IRQ) { | ||
765 | /* ok.. capture is active */ | ||
766 | if (chip->active & ADC1) | ||
767 | snd_pcm_period_elapsed(chip->capture_a_substream); | ||
768 | /* ok.. playback2 is active */ | ||
769 | else if (chip->active & DAC1) | ||
770 | snd_pcm_period_elapsed(chip->playback_b_substream); | ||
771 | /* ack interrupt */ | ||
772 | inb(chip->port + 0x0E); | ||
773 | } | ||
774 | |||
775 | /* MPU */ | ||
776 | if ((status & MPU_IRQ) && chip->rmidi) | ||
777 | snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); | ||
778 | |||
779 | /* Hardware volume */ | ||
780 | if (status & HWV_IRQ) { | ||
781 | int split = snd_es18xx_mixer_read(chip, 0x64) & 0x80; | ||
782 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); | ||
783 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); | ||
784 | if (!split) { | ||
785 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); | ||
786 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); | ||
787 | } | ||
788 | /* ack interrupt */ | ||
789 | snd_es18xx_mixer_write(chip, 0x66, 0x00); | ||
790 | } | ||
791 | return IRQ_HANDLED; | ||
792 | } | ||
793 | |||
794 | static snd_pcm_uframes_t snd_es18xx_playback_pointer(snd_pcm_substream_t * substream) | ||
795 | { | ||
796 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
797 | int pos; | ||
798 | |||
799 | if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { | ||
800 | if (!(chip->active & DAC2)) | ||
801 | return 0; | ||
802 | pos = snd_dma_pointer(chip->dma2, chip->dma2_size); | ||
803 | return pos >> chip->dma2_shift; | ||
804 | } else { | ||
805 | if (!(chip->active & DAC1)) | ||
806 | return 0; | ||
807 | pos = snd_dma_pointer(chip->dma1, chip->dma1_size); | ||
808 | return pos >> chip->dma1_shift; | ||
809 | } | ||
810 | } | ||
811 | |||
812 | static snd_pcm_uframes_t snd_es18xx_capture_pointer(snd_pcm_substream_t * substream) | ||
813 | { | ||
814 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
815 | int pos; | ||
816 | |||
817 | if (!(chip->active & ADC1)) | ||
818 | return 0; | ||
819 | pos = snd_dma_pointer(chip->dma1, chip->dma1_size); | ||
820 | return pos >> chip->dma1_shift; | ||
821 | } | ||
822 | |||
823 | static snd_pcm_hardware_t snd_es18xx_playback = | ||
824 | { | ||
825 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
826 | SNDRV_PCM_INFO_RESUME | | ||
827 | SNDRV_PCM_INFO_MMAP_VALID), | ||
828 | .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | | ||
829 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), | ||
830 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
831 | .rate_min = 4000, | ||
832 | .rate_max = 48000, | ||
833 | .channels_min = 1, | ||
834 | .channels_max = 2, | ||
835 | .buffer_bytes_max = 65536, | ||
836 | .period_bytes_min = 64, | ||
837 | .period_bytes_max = 65536, | ||
838 | .periods_min = 1, | ||
839 | .periods_max = 1024, | ||
840 | .fifo_size = 0, | ||
841 | }; | ||
842 | |||
843 | static snd_pcm_hardware_t snd_es18xx_capture = | ||
844 | { | ||
845 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
846 | SNDRV_PCM_INFO_RESUME | | ||
847 | SNDRV_PCM_INFO_MMAP_VALID), | ||
848 | .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | | ||
849 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), | ||
850 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
851 | .rate_min = 4000, | ||
852 | .rate_max = 48000, | ||
853 | .channels_min = 1, | ||
854 | .channels_max = 2, | ||
855 | .buffer_bytes_max = 65536, | ||
856 | .period_bytes_min = 64, | ||
857 | .period_bytes_max = 65536, | ||
858 | .periods_min = 1, | ||
859 | .periods_max = 1024, | ||
860 | .fifo_size = 0, | ||
861 | }; | ||
862 | |||
863 | static int snd_es18xx_playback_open(snd_pcm_substream_t * substream) | ||
864 | { | ||
865 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
866 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
867 | |||
868 | if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { | ||
869 | if ((chip->caps & ES18XX_DUPLEX_MONO) && | ||
870 | chip->capture_a_substream && | ||
871 | chip->capture_a_substream->runtime->channels != 1) | ||
872 | return -EAGAIN; | ||
873 | chip->playback_a_substream = substream; | ||
874 | } else if (substream->number <= 1) { | ||
875 | if (chip->capture_a_substream) | ||
876 | return -EAGAIN; | ||
877 | chip->playback_b_substream = substream; | ||
878 | } else { | ||
879 | snd_BUG(); | ||
880 | return -EINVAL; | ||
881 | } | ||
882 | substream->runtime->hw = snd_es18xx_playback; | ||
883 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
884 | (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); | ||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static int snd_es18xx_capture_open(snd_pcm_substream_t * substream) | ||
889 | { | ||
890 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
891 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
892 | |||
893 | if (chip->playback_b_substream) | ||
894 | return -EAGAIN; | ||
895 | if ((chip->caps & ES18XX_DUPLEX_MONO) && | ||
896 | chip->playback_a_substream && | ||
897 | chip->playback_a_substream->runtime->channels != 1) | ||
898 | return -EAGAIN; | ||
899 | chip->capture_a_substream = substream; | ||
900 | substream->runtime->hw = snd_es18xx_capture; | ||
901 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
902 | (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); | ||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | static int snd_es18xx_playback_close(snd_pcm_substream_t * substream) | ||
907 | { | ||
908 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
909 | |||
910 | if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) | ||
911 | chip->playback_a_substream = NULL; | ||
912 | else | ||
913 | chip->playback_b_substream = NULL; | ||
914 | |||
915 | snd_pcm_lib_free_pages(substream); | ||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | static int snd_es18xx_capture_close(snd_pcm_substream_t * substream) | ||
920 | { | ||
921 | es18xx_t *chip = snd_pcm_substream_chip(substream); | ||
922 | |||
923 | chip->capture_a_substream = NULL; | ||
924 | snd_pcm_lib_free_pages(substream); | ||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * MIXER part | ||
930 | */ | ||
931 | |||
932 | static int snd_es18xx_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
933 | { | ||
934 | static char *texts[8] = { | ||
935 | "Mic", "Mic Master", "CD", "AOUT", | ||
936 | "Mic1", "Mix", "Line", "Master" | ||
937 | }; | ||
938 | |||
939 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
940 | uinfo->count = 1; | ||
941 | uinfo->value.enumerated.items = 8; | ||
942 | if (uinfo->value.enumerated.item > 7) | ||
943 | uinfo->value.enumerated.item = 7; | ||
944 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static int snd_es18xx_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
949 | { | ||
950 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
951 | ucontrol->value.enumerated.item[0] = snd_es18xx_mixer_read(chip, 0x1c) & 0x07; | ||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | static int snd_es18xx_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
956 | { | ||
957 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
958 | unsigned char val = ucontrol->value.enumerated.item[0]; | ||
959 | |||
960 | if (val > 7) | ||
961 | return -EINVAL; | ||
962 | return snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val; | ||
963 | } | ||
964 | |||
965 | static int snd_es18xx_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
966 | { | ||
967 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
968 | uinfo->count = 1; | ||
969 | uinfo->value.integer.min = 0; | ||
970 | uinfo->value.integer.max = 1; | ||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int snd_es18xx_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
975 | { | ||
976 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
977 | unsigned char val = snd_es18xx_mixer_read(chip, 0x50); | ||
978 | ucontrol->value.integer.value[0] = !!(val & 8); | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static int snd_es18xx_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
983 | { | ||
984 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
985 | unsigned char oval, nval; | ||
986 | int change; | ||
987 | nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; | ||
988 | oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c; | ||
989 | change = nval != oval; | ||
990 | if (change) { | ||
991 | snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04); | ||
992 | snd_es18xx_mixer_write(chip, 0x50, nval); | ||
993 | } | ||
994 | return change; | ||
995 | } | ||
996 | |||
997 | static int snd_es18xx_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
998 | { | ||
999 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1000 | uinfo->count = 2; | ||
1001 | uinfo->value.integer.min = 0; | ||
1002 | uinfo->value.integer.max = 63; | ||
1003 | return 0; | ||
1004 | } | ||
1005 | |||
1006 | static int snd_es18xx_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1007 | { | ||
1008 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1009 | ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f; | ||
1010 | ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f; | ||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | static int snd_es18xx_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1015 | { | ||
1016 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
1017 | uinfo->count = 2; | ||
1018 | uinfo->value.integer.min = 0; | ||
1019 | uinfo->value.integer.max = 1; | ||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | static int snd_es18xx_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1024 | { | ||
1025 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1026 | ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40); | ||
1027 | ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40); | ||
1028 | return 0; | ||
1029 | } | ||
1030 | |||
1031 | static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol) | ||
1032 | { | ||
1033 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1034 | chip->master_volume = NULL; | ||
1035 | chip->master_switch = NULL; | ||
1036 | chip->hw_volume = NULL; | ||
1037 | chip->hw_switch = NULL; | ||
1038 | } | ||
1039 | |||
1040 | static int snd_es18xx_reg_bits(es18xx_t *chip, unsigned char reg, | ||
1041 | unsigned char mask, unsigned char val) | ||
1042 | { | ||
1043 | if (reg < 0xa0) | ||
1044 | return snd_es18xx_mixer_bits(chip, reg, mask, val); | ||
1045 | else | ||
1046 | return snd_es18xx_bits(chip, reg, mask, val); | ||
1047 | } | ||
1048 | |||
1049 | static int snd_es18xx_reg_read(es18xx_t *chip, unsigned char reg) | ||
1050 | { | ||
1051 | if (reg < 0xa0) | ||
1052 | return snd_es18xx_mixer_read(chip, reg); | ||
1053 | else | ||
1054 | return snd_es18xx_read(chip, reg); | ||
1055 | } | ||
1056 | |||
1057 | #define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
1058 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
1059 | .info = snd_es18xx_info_single, \ | ||
1060 | .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \ | ||
1061 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
1062 | |||
1063 | static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1064 | { | ||
1065 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1066 | |||
1067 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1068 | uinfo->count = 1; | ||
1069 | uinfo->value.integer.min = 0; | ||
1070 | uinfo->value.integer.max = mask; | ||
1071 | return 0; | ||
1072 | } | ||
1073 | |||
1074 | static int snd_es18xx_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1075 | { | ||
1076 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1077 | int reg = kcontrol->private_value & 0xff; | ||
1078 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1079 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1080 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1081 | int val; | ||
1082 | |||
1083 | val = snd_es18xx_reg_read(chip, reg); | ||
1084 | ucontrol->value.integer.value[0] = (val >> shift) & mask; | ||
1085 | if (invert) | ||
1086 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1090 | static int snd_es18xx_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1091 | { | ||
1092 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1093 | int reg = kcontrol->private_value & 0xff; | ||
1094 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1095 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1096 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1097 | unsigned char val; | ||
1098 | |||
1099 | val = (ucontrol->value.integer.value[0] & mask); | ||
1100 | if (invert) | ||
1101 | val = mask - val; | ||
1102 | mask <<= shift; | ||
1103 | val <<= shift; | ||
1104 | return snd_es18xx_reg_bits(chip, reg, mask, val) != val; | ||
1105 | } | ||
1106 | |||
1107 | #define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
1108 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
1109 | .info = snd_es18xx_info_double, \ | ||
1110 | .get = snd_es18xx_get_double, .put = snd_es18xx_put_double, \ | ||
1111 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
1112 | |||
1113 | static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1114 | { | ||
1115 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1116 | |||
1117 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1118 | uinfo->count = 2; | ||
1119 | uinfo->value.integer.min = 0; | ||
1120 | uinfo->value.integer.max = mask; | ||
1121 | return 0; | ||
1122 | } | ||
1123 | |||
1124 | static int snd_es18xx_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1125 | { | ||
1126 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1127 | int left_reg = kcontrol->private_value & 0xff; | ||
1128 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1129 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1130 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1131 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1132 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1133 | unsigned char left, right; | ||
1134 | |||
1135 | left = snd_es18xx_reg_read(chip, left_reg); | ||
1136 | if (left_reg != right_reg) | ||
1137 | right = snd_es18xx_reg_read(chip, right_reg); | ||
1138 | else | ||
1139 | right = left; | ||
1140 | ucontrol->value.integer.value[0] = (left >> shift_left) & mask; | ||
1141 | ucontrol->value.integer.value[1] = (right >> shift_right) & mask; | ||
1142 | if (invert) { | ||
1143 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1144 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
1145 | } | ||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | static int snd_es18xx_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1150 | { | ||
1151 | es18xx_t *chip = snd_kcontrol_chip(kcontrol); | ||
1152 | int left_reg = kcontrol->private_value & 0xff; | ||
1153 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1154 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1155 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1156 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1157 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1158 | int change; | ||
1159 | unsigned char val1, val2, mask1, mask2; | ||
1160 | |||
1161 | val1 = ucontrol->value.integer.value[0] & mask; | ||
1162 | val2 = ucontrol->value.integer.value[1] & mask; | ||
1163 | if (invert) { | ||
1164 | val1 = mask - val1; | ||
1165 | val2 = mask - val2; | ||
1166 | } | ||
1167 | val1 <<= shift_left; | ||
1168 | val2 <<= shift_right; | ||
1169 | mask1 = mask << shift_left; | ||
1170 | mask2 = mask << shift_right; | ||
1171 | if (left_reg != right_reg) { | ||
1172 | change = 0; | ||
1173 | if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1) | ||
1174 | change = 1; | ||
1175 | if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2) | ||
1176 | change = 1; | ||
1177 | } else { | ||
1178 | change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, | ||
1179 | val1 | val2) != (val1 | val2)); | ||
1180 | } | ||
1181 | return change; | ||
1182 | } | ||
1183 | |||
1184 | static snd_kcontrol_new_t snd_es18xx_base_controls[] = { | ||
1185 | ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), | ||
1186 | ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), | ||
1187 | ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), | ||
1188 | ES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), | ||
1189 | ES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), | ||
1190 | ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), | ||
1191 | ES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), | ||
1192 | ES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), | ||
1193 | ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0), | ||
1194 | ES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), | ||
1195 | ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), | ||
1196 | ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), | ||
1197 | { | ||
1198 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1199 | .name = "Capture Source", | ||
1200 | .info = snd_es18xx_info_mux, | ||
1201 | .get = snd_es18xx_get_mux, | ||
1202 | .put = snd_es18xx_put_mux, | ||
1203 | } | ||
1204 | }; | ||
1205 | |||
1206 | static snd_kcontrol_new_t snd_es18xx_mono_in_control = | ||
1207 | ES18XX_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0); | ||
1208 | |||
1209 | static snd_kcontrol_new_t snd_es18xx_recmix_controls[] = { | ||
1210 | ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), | ||
1211 | ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), | ||
1212 | ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), | ||
1213 | ES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), | ||
1214 | ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), | ||
1215 | ES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), | ||
1216 | ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0) | ||
1217 | }; | ||
1218 | |||
1219 | static snd_kcontrol_new_t snd_es18xx_pcm1_controls[] = { | ||
1220 | ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0), | ||
1221 | }; | ||
1222 | |||
1223 | static snd_kcontrol_new_t snd_es18xx_pcm2_controls[] = { | ||
1224 | ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), | ||
1225 | ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0) | ||
1226 | }; | ||
1227 | |||
1228 | static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = { | ||
1229 | ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), | ||
1230 | { | ||
1231 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1232 | .name = "3D Control - Switch", | ||
1233 | .info = snd_es18xx_info_spatializer_enable, | ||
1234 | .get = snd_es18xx_get_spatializer_enable, | ||
1235 | .put = snd_es18xx_put_spatializer_enable, | ||
1236 | } | ||
1237 | }; | ||
1238 | |||
1239 | static snd_kcontrol_new_t snd_es18xx_micpre1_control = | ||
1240 | ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0); | ||
1241 | |||
1242 | static snd_kcontrol_new_t snd_es18xx_micpre2_control = | ||
1243 | ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0); | ||
1244 | |||
1245 | static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = { | ||
1246 | { | ||
1247 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1248 | .name = "Hardware Master Playback Volume", | ||
1249 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
1250 | .info = snd_es18xx_info_hw_volume, | ||
1251 | .get = snd_es18xx_get_hw_volume, | ||
1252 | }, | ||
1253 | { | ||
1254 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1255 | .name = "Hardware Master Playback Switch", | ||
1256 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
1257 | .info = snd_es18xx_info_hw_switch, | ||
1258 | .get = snd_es18xx_get_hw_switch, | ||
1259 | }, | ||
1260 | ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), | ||
1261 | }; | ||
1262 | |||
1263 | #if 0 | ||
1264 | static int __devinit snd_es18xx_config_read(es18xx_t *chip, unsigned char reg) | ||
1265 | { | ||
1266 | int data; | ||
1267 | unsigned long flags; | ||
1268 | spin_lock_irqsave(&chip->ctrl_lock, flags); | ||
1269 | outb(reg, chip->ctrl_port); | ||
1270 | data = inb(chip->ctrl_port + 1); | ||
1271 | spin_unlock_irqrestore(&chip->ctrl_lock, flags); | ||
1272 | return data; | ||
1273 | } | ||
1274 | #endif | ||
1275 | |||
1276 | static void __devinit snd_es18xx_config_write(es18xx_t *chip, | ||
1277 | unsigned char reg, unsigned char data) | ||
1278 | { | ||
1279 | /* No need for spinlocks, this function is used only in | ||
1280 | otherwise protected init code */ | ||
1281 | outb(reg, chip->ctrl_port); | ||
1282 | outb(data, chip->ctrl_port + 1); | ||
1283 | #ifdef REG_DEBUG | ||
1284 | snd_printk("Config reg %02x set to %02x\n", reg, data); | ||
1285 | #endif | ||
1286 | } | ||
1287 | |||
1288 | static int __devinit snd_es18xx_initialize(es18xx_t *chip) | ||
1289 | { | ||
1290 | int mask = 0; | ||
1291 | |||
1292 | /* enable extended mode */ | ||
1293 | snd_es18xx_dsp_command(chip, 0xC6); | ||
1294 | /* Reset mixer registers */ | ||
1295 | snd_es18xx_mixer_write(chip, 0x00, 0x00); | ||
1296 | |||
1297 | /* Audio 1 DMA demand mode (4 bytes/request) */ | ||
1298 | snd_es18xx_write(chip, 0xB9, 2); | ||
1299 | if (chip->caps & ES18XX_CONTROL) { | ||
1300 | /* Hardware volume IRQ */ | ||
1301 | snd_es18xx_config_write(chip, 0x27, chip->irq); | ||
1302 | if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { | ||
1303 | /* FM I/O */ | ||
1304 | snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8); | ||
1305 | snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff); | ||
1306 | } | ||
1307 | if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { | ||
1308 | /* MPU-401 I/O */ | ||
1309 | snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8); | ||
1310 | snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff); | ||
1311 | /* MPU-401 IRQ */ | ||
1312 | snd_es18xx_config_write(chip, 0x28, chip->irq); | ||
1313 | } | ||
1314 | /* Audio1 IRQ */ | ||
1315 | snd_es18xx_config_write(chip, 0x70, chip->irq); | ||
1316 | /* Audio2 IRQ */ | ||
1317 | snd_es18xx_config_write(chip, 0x72, chip->irq); | ||
1318 | /* Audio1 DMA */ | ||
1319 | snd_es18xx_config_write(chip, 0x74, chip->dma1); | ||
1320 | /* Audio2 DMA */ | ||
1321 | snd_es18xx_config_write(chip, 0x75, chip->dma2); | ||
1322 | |||
1323 | /* Enable Audio 1 IRQ */ | ||
1324 | snd_es18xx_write(chip, 0xB1, 0x50); | ||
1325 | /* Enable Audio 2 IRQ */ | ||
1326 | snd_es18xx_mixer_write(chip, 0x7A, 0x40); | ||
1327 | /* Enable Audio 1 DMA */ | ||
1328 | snd_es18xx_write(chip, 0xB2, 0x50); | ||
1329 | /* Enable MPU and hardware volume interrupt */ | ||
1330 | snd_es18xx_mixer_write(chip, 0x64, 0x42); | ||
1331 | } | ||
1332 | else { | ||
1333 | int irqmask, dma1mask, dma2mask; | ||
1334 | switch (chip->irq) { | ||
1335 | case 2: | ||
1336 | case 9: | ||
1337 | irqmask = 0; | ||
1338 | break; | ||
1339 | case 5: | ||
1340 | irqmask = 1; | ||
1341 | break; | ||
1342 | case 7: | ||
1343 | irqmask = 2; | ||
1344 | break; | ||
1345 | case 10: | ||
1346 | irqmask = 3; | ||
1347 | break; | ||
1348 | default: | ||
1349 | snd_printk("invalid irq %d\n", chip->irq); | ||
1350 | return -ENODEV; | ||
1351 | } | ||
1352 | switch (chip->dma1) { | ||
1353 | case 0: | ||
1354 | dma1mask = 1; | ||
1355 | break; | ||
1356 | case 1: | ||
1357 | dma1mask = 2; | ||
1358 | break; | ||
1359 | case 3: | ||
1360 | dma1mask = 3; | ||
1361 | break; | ||
1362 | default: | ||
1363 | snd_printk("invalid dma1 %d\n", chip->dma1); | ||
1364 | return -ENODEV; | ||
1365 | } | ||
1366 | switch (chip->dma2) { | ||
1367 | case 0: | ||
1368 | dma2mask = 0; | ||
1369 | break; | ||
1370 | case 1: | ||
1371 | dma2mask = 1; | ||
1372 | break; | ||
1373 | case 3: | ||
1374 | dma2mask = 2; | ||
1375 | break; | ||
1376 | case 5: | ||
1377 | dma2mask = 3; | ||
1378 | break; | ||
1379 | default: | ||
1380 | snd_printk("invalid dma2 %d\n", chip->dma2); | ||
1381 | return -ENODEV; | ||
1382 | } | ||
1383 | |||
1384 | /* Enable and set Audio 1 IRQ */ | ||
1385 | snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); | ||
1386 | /* Enable and set Audio 1 DMA */ | ||
1387 | snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); | ||
1388 | /* Set Audio 2 DMA */ | ||
1389 | snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); | ||
1390 | /* Enable Audio 2 IRQ and DMA | ||
1391 | Set capture mixer input */ | ||
1392 | snd_es18xx_mixer_write(chip, 0x7A, 0x68); | ||
1393 | /* Enable and set hardware volume interrupt */ | ||
1394 | snd_es18xx_mixer_write(chip, 0x64, 0x06); | ||
1395 | if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { | ||
1396 | /* MPU401 share irq with audio | ||
1397 | Joystick enabled | ||
1398 | FM enabled */ | ||
1399 | snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1); | ||
1400 | } | ||
1401 | snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); | ||
1402 | } | ||
1403 | if (chip->caps & ES18XX_NEW_RATE) { | ||
1404 | /* Change behaviour of register A1 | ||
1405 | 4x oversampling | ||
1406 | 2nd channel DAC asynchronous */ | ||
1407 | snd_es18xx_mixer_write(chip, 0x71, 0x32); | ||
1408 | } | ||
1409 | if (!(chip->caps & ES18XX_PCM2)) { | ||
1410 | /* Enable DMA FIFO */ | ||
1411 | snd_es18xx_write(chip, 0xB7, 0x80); | ||
1412 | } | ||
1413 | if (chip->caps & ES18XX_SPATIALIZER) { | ||
1414 | /* Set spatializer parameters to recommended values */ | ||
1415 | snd_es18xx_mixer_write(chip, 0x54, 0x8f); | ||
1416 | snd_es18xx_mixer_write(chip, 0x56, 0x95); | ||
1417 | snd_es18xx_mixer_write(chip, 0x58, 0x94); | ||
1418 | snd_es18xx_mixer_write(chip, 0x5a, 0x80); | ||
1419 | } | ||
1420 | /* Mute input source */ | ||
1421 | if (chip->caps & ES18XX_MUTEREC) | ||
1422 | mask = 0x10; | ||
1423 | if (chip->caps & ES18XX_RECMIX) | ||
1424 | snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); | ||
1425 | else { | ||
1426 | snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); | ||
1427 | snd_es18xx_write(chip, 0xb4, 0x00); | ||
1428 | } | ||
1429 | #ifndef AVOID_POPS | ||
1430 | /* Enable PCM output */ | ||
1431 | snd_es18xx_dsp_command(chip, 0xD1); | ||
1432 | #endif | ||
1433 | |||
1434 | return 0; | ||
1435 | } | ||
1436 | |||
1437 | static int __devinit snd_es18xx_identify(es18xx_t *chip) | ||
1438 | { | ||
1439 | int hi,lo; | ||
1440 | |||
1441 | /* reset */ | ||
1442 | if (snd_es18xx_reset(chip) < 0) { | ||
1443 | snd_printk("reset at 0x%lx failed!!!\n", chip->port); | ||
1444 | return -ENODEV; | ||
1445 | } | ||
1446 | |||
1447 | snd_es18xx_dsp_command(chip, 0xe7); | ||
1448 | hi = snd_es18xx_dsp_get_byte(chip); | ||
1449 | if (hi < 0) { | ||
1450 | return hi; | ||
1451 | } | ||
1452 | lo = snd_es18xx_dsp_get_byte(chip); | ||
1453 | if ((lo & 0xf0) != 0x80) { | ||
1454 | return -ENODEV; | ||
1455 | } | ||
1456 | if (hi == 0x48) { | ||
1457 | chip->version = 0x488; | ||
1458 | return 0; | ||
1459 | } | ||
1460 | if (hi != 0x68) { | ||
1461 | return -ENODEV; | ||
1462 | } | ||
1463 | if ((lo & 0x0f) < 8) { | ||
1464 | chip->version = 0x688; | ||
1465 | return 0; | ||
1466 | } | ||
1467 | |||
1468 | outb(0x40, chip->port + 0x04); | ||
1469 | hi = inb(chip->port + 0x05); | ||
1470 | lo = inb(chip->port + 0x05); | ||
1471 | if (hi != lo) { | ||
1472 | chip->version = hi << 8 | lo; | ||
1473 | chip->ctrl_port = inb(chip->port + 0x05) << 8; | ||
1474 | chip->ctrl_port += inb(chip->port + 0x05); | ||
1475 | |||
1476 | if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) { | ||
1477 | snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port); | ||
1478 | return -EBUSY; | ||
1479 | } | ||
1480 | |||
1481 | return 0; | ||
1482 | } | ||
1483 | |||
1484 | /* If has Hardware volume */ | ||
1485 | if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { | ||
1486 | /* If has Audio2 */ | ||
1487 | if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { | ||
1488 | /* If has volume count */ | ||
1489 | if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { | ||
1490 | chip->version = 0x1887; | ||
1491 | } else { | ||
1492 | chip->version = 0x1888; | ||
1493 | } | ||
1494 | } else { | ||
1495 | chip->version = 0x1788; | ||
1496 | } | ||
1497 | } | ||
1498 | else | ||
1499 | chip->version = 0x1688; | ||
1500 | return 0; | ||
1501 | } | ||
1502 | |||
1503 | static int __devinit snd_es18xx_probe(es18xx_t *chip) | ||
1504 | { | ||
1505 | if (snd_es18xx_identify(chip) < 0) { | ||
1506 | snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); | ||
1507 | return -ENODEV; | ||
1508 | } | ||
1509 | |||
1510 | switch (chip->version) { | ||
1511 | case 0x1868: | ||
1512 | chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_HWV; | ||
1513 | break; | ||
1514 | case 0x1869: | ||
1515 | chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; | ||
1516 | break; | ||
1517 | case 0x1878: | ||
1518 | chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; | ||
1519 | break; | ||
1520 | case 0x1879: | ||
1521 | chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; | ||
1522 | break; | ||
1523 | case 0x1887: | ||
1524 | chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; | ||
1525 | break; | ||
1526 | case 0x1888: | ||
1527 | chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; | ||
1528 | break; | ||
1529 | default: | ||
1530 | snd_printk("[0x%lx] unsupported chip ES%x\n", | ||
1531 | chip->port, chip->version); | ||
1532 | return -ENODEV; | ||
1533 | } | ||
1534 | |||
1535 | snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); | ||
1536 | |||
1537 | if (chip->dma1 == chip->dma2) | ||
1538 | chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME); | ||
1539 | |||
1540 | return snd_es18xx_initialize(chip); | ||
1541 | } | ||
1542 | |||
1543 | static snd_pcm_ops_t snd_es18xx_playback_ops = { | ||
1544 | .open = snd_es18xx_playback_open, | ||
1545 | .close = snd_es18xx_playback_close, | ||
1546 | .ioctl = snd_pcm_lib_ioctl, | ||
1547 | .hw_params = snd_es18xx_playback_hw_params, | ||
1548 | .hw_free = snd_es18xx_pcm_hw_free, | ||
1549 | .prepare = snd_es18xx_playback_prepare, | ||
1550 | .trigger = snd_es18xx_playback_trigger, | ||
1551 | .pointer = snd_es18xx_playback_pointer, | ||
1552 | }; | ||
1553 | |||
1554 | static snd_pcm_ops_t snd_es18xx_capture_ops = { | ||
1555 | .open = snd_es18xx_capture_open, | ||
1556 | .close = snd_es18xx_capture_close, | ||
1557 | .ioctl = snd_pcm_lib_ioctl, | ||
1558 | .hw_params = snd_es18xx_capture_hw_params, | ||
1559 | .hw_free = snd_es18xx_pcm_hw_free, | ||
1560 | .prepare = snd_es18xx_capture_prepare, | ||
1561 | .trigger = snd_es18xx_capture_trigger, | ||
1562 | .pointer = snd_es18xx_capture_pointer, | ||
1563 | }; | ||
1564 | |||
1565 | static void snd_es18xx_pcm_free(snd_pcm_t *pcm) | ||
1566 | { | ||
1567 | es18xx_t *codec = pcm->private_data; | ||
1568 | codec->pcm = NULL; | ||
1569 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
1570 | } | ||
1571 | |||
1572 | static int __devinit snd_es18xx_pcm(es18xx_t *chip, int device, snd_pcm_t ** rpcm) | ||
1573 | { | ||
1574 | snd_pcm_t *pcm; | ||
1575 | char str[16]; | ||
1576 | int err; | ||
1577 | |||
1578 | if (rpcm) | ||
1579 | *rpcm = NULL; | ||
1580 | sprintf(str, "ES%x", chip->version); | ||
1581 | if (chip->caps & ES18XX_PCM2) { | ||
1582 | err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm); | ||
1583 | } else { | ||
1584 | err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm); | ||
1585 | } | ||
1586 | if (err < 0) | ||
1587 | return err; | ||
1588 | |||
1589 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); | ||
1590 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); | ||
1591 | |||
1592 | /* global setup */ | ||
1593 | pcm->private_data = chip; | ||
1594 | pcm->private_free = snd_es18xx_pcm_free; | ||
1595 | pcm->info_flags = 0; | ||
1596 | if (chip->caps & ES18XX_DUPLEX_SAME) | ||
1597 | pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
1598 | if (! (chip->caps & ES18XX_PCM2)) | ||
1599 | pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; | ||
1600 | sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); | ||
1601 | chip->pcm = pcm; | ||
1602 | |||
1603 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
1604 | snd_dma_isa_data(), | ||
1605 | 64*1024, | ||
1606 | chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); | ||
1607 | |||
1608 | if (rpcm) | ||
1609 | *rpcm = pcm; | ||
1610 | return 0; | ||
1611 | } | ||
1612 | |||
1613 | /* Power Management support functions */ | ||
1614 | #ifdef CONFIG_PM | ||
1615 | static int snd_es18xx_suspend(snd_card_t *card, pm_message_t state) | ||
1616 | { | ||
1617 | es18xx_t *chip = card->pm_private_data; | ||
1618 | |||
1619 | snd_pcm_suspend_all(chip->pcm); | ||
1620 | |||
1621 | /* power down */ | ||
1622 | chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); | ||
1623 | chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); | ||
1624 | snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); | ||
1625 | snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); | ||
1626 | |||
1627 | return 0; | ||
1628 | } | ||
1629 | |||
1630 | static int snd_es18xx_resume(snd_card_t *card) | ||
1631 | { | ||
1632 | es18xx_t *chip = card->pm_private_data; | ||
1633 | |||
1634 | /* restore PM register, we won't wake till (not 0x07) i/o activity though */ | ||
1635 | snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); | ||
1636 | |||
1637 | return 0; | ||
1638 | } | ||
1639 | #endif /* CONFIG_PM */ | ||
1640 | |||
1641 | static int snd_es18xx_free(es18xx_t *chip) | ||
1642 | { | ||
1643 | if (chip->res_port) { | ||
1644 | release_resource(chip->res_port); | ||
1645 | kfree_nocheck(chip->res_port); | ||
1646 | } | ||
1647 | if (chip->res_ctrl_port) { | ||
1648 | release_resource(chip->res_ctrl_port); | ||
1649 | kfree_nocheck(chip->res_ctrl_port); | ||
1650 | } | ||
1651 | if (chip->res_mpu_port) { | ||
1652 | release_resource(chip->res_mpu_port); | ||
1653 | kfree_nocheck(chip->res_mpu_port); | ||
1654 | } | ||
1655 | if (chip->irq >= 0) | ||
1656 | free_irq(chip->irq, (void *) chip); | ||
1657 | if (chip->dma1 >= 0) { | ||
1658 | disable_dma(chip->dma1); | ||
1659 | free_dma(chip->dma1); | ||
1660 | } | ||
1661 | if (chip->dma2 >= 0 && chip->dma1 != chip->dma2) { | ||
1662 | disable_dma(chip->dma2); | ||
1663 | free_dma(chip->dma2); | ||
1664 | } | ||
1665 | kfree(chip); | ||
1666 | return 0; | ||
1667 | } | ||
1668 | |||
1669 | static int snd_es18xx_dev_free(snd_device_t *device) | ||
1670 | { | ||
1671 | es18xx_t *chip = device->device_data; | ||
1672 | return snd_es18xx_free(chip); | ||
1673 | } | ||
1674 | |||
1675 | static int __devinit snd_es18xx_new_device(snd_card_t * card, | ||
1676 | unsigned long port, | ||
1677 | unsigned long mpu_port, | ||
1678 | unsigned long fm_port, | ||
1679 | int irq, int dma1, int dma2, | ||
1680 | es18xx_t ** rchip) | ||
1681 | { | ||
1682 | es18xx_t *chip; | ||
1683 | static snd_device_ops_t ops = { | ||
1684 | .dev_free = snd_es18xx_dev_free, | ||
1685 | }; | ||
1686 | int err; | ||
1687 | |||
1688 | *rchip = NULL; | ||
1689 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
1690 | if (chip == NULL) | ||
1691 | return -ENOMEM; | ||
1692 | spin_lock_init(&chip->reg_lock); | ||
1693 | spin_lock_init(&chip->mixer_lock); | ||
1694 | spin_lock_init(&chip->ctrl_lock); | ||
1695 | chip->card = card; | ||
1696 | chip->port = port; | ||
1697 | chip->mpu_port = mpu_port; | ||
1698 | chip->fm_port = fm_port; | ||
1699 | chip->irq = -1; | ||
1700 | chip->dma1 = -1; | ||
1701 | chip->dma2 = -1; | ||
1702 | chip->audio2_vol = 0x00; | ||
1703 | chip->active = 0; | ||
1704 | |||
1705 | if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) { | ||
1706 | snd_es18xx_free(chip); | ||
1707 | snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); | ||
1708 | return -EBUSY; | ||
1709 | } | ||
1710 | |||
1711 | if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) { | ||
1712 | snd_es18xx_free(chip); | ||
1713 | snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); | ||
1714 | return -EBUSY; | ||
1715 | } | ||
1716 | chip->irq = irq; | ||
1717 | |||
1718 | if (request_dma(dma1, "ES18xx DMA 1")) { | ||
1719 | snd_es18xx_free(chip); | ||
1720 | snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1); | ||
1721 | return -EBUSY; | ||
1722 | } | ||
1723 | chip->dma1 = dma1; | ||
1724 | |||
1725 | if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) { | ||
1726 | snd_es18xx_free(chip); | ||
1727 | snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2); | ||
1728 | return -EBUSY; | ||
1729 | } | ||
1730 | chip->dma2 = dma2; | ||
1731 | |||
1732 | if (snd_es18xx_probe(chip) < 0) { | ||
1733 | snd_es18xx_free(chip); | ||
1734 | return -ENODEV; | ||
1735 | } | ||
1736 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
1737 | snd_es18xx_free(chip); | ||
1738 | return err; | ||
1739 | } | ||
1740 | *rchip = chip; | ||
1741 | return 0; | ||
1742 | } | ||
1743 | |||
1744 | static int __devinit snd_es18xx_mixer(es18xx_t *chip) | ||
1745 | { | ||
1746 | snd_card_t *card; | ||
1747 | int err; | ||
1748 | unsigned int idx; | ||
1749 | |||
1750 | card = chip->card; | ||
1751 | |||
1752 | strcpy(card->mixername, chip->pcm->name); | ||
1753 | |||
1754 | for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) { | ||
1755 | snd_kcontrol_t *kctl; | ||
1756 | kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip); | ||
1757 | if (chip->caps & ES18XX_HWV) { | ||
1758 | switch (idx) { | ||
1759 | case 0: | ||
1760 | chip->master_volume = kctl; | ||
1761 | kctl->private_free = snd_es18xx_hwv_free; | ||
1762 | break; | ||
1763 | case 1: | ||
1764 | chip->master_switch = kctl; | ||
1765 | kctl->private_free = snd_es18xx_hwv_free; | ||
1766 | break; | ||
1767 | } | ||
1768 | } | ||
1769 | if ((err = snd_ctl_add(card, kctl)) < 0) | ||
1770 | return err; | ||
1771 | } | ||
1772 | if (chip->caps & ES18XX_PCM2) { | ||
1773 | for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) { | ||
1774 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0) | ||
1775 | return err; | ||
1776 | } | ||
1777 | } else { | ||
1778 | for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) { | ||
1779 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0) | ||
1780 | return err; | ||
1781 | } | ||
1782 | } | ||
1783 | |||
1784 | if (chip->caps & ES18XX_MONO) { | ||
1785 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_mono_in_control, chip))) < 0) | ||
1786 | return err; | ||
1787 | } | ||
1788 | if (chip->caps & ES18XX_RECMIX) { | ||
1789 | for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) { | ||
1790 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0) | ||
1791 | return err; | ||
1792 | } | ||
1793 | } | ||
1794 | switch (chip->version) { | ||
1795 | default: | ||
1796 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0) | ||
1797 | return err; | ||
1798 | break; | ||
1799 | case 0x1869: | ||
1800 | case 0x1879: | ||
1801 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0) | ||
1802 | return err; | ||
1803 | break; | ||
1804 | } | ||
1805 | if (chip->caps & ES18XX_SPATIALIZER) { | ||
1806 | for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) { | ||
1807 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0) | ||
1808 | return err; | ||
1809 | } | ||
1810 | } | ||
1811 | if (chip->caps & ES18XX_HWV) { | ||
1812 | for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_hw_volume_controls); idx++) { | ||
1813 | snd_kcontrol_t *kctl; | ||
1814 | kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip); | ||
1815 | if (idx == 0) | ||
1816 | chip->hw_volume = kctl; | ||
1817 | else | ||
1818 | chip->hw_switch = kctl; | ||
1819 | kctl->private_free = snd_es18xx_hwv_free; | ||
1820 | if ((err = snd_ctl_add(card, kctl)) < 0) | ||
1821 | return err; | ||
1822 | |||
1823 | } | ||
1824 | } | ||
1825 | return 0; | ||
1826 | } | ||
1827 | |||
1828 | |||
1829 | /* Card level */ | ||
1830 | |||
1831 | MODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>"); | ||
1832 | MODULE_DESCRIPTION("ESS ES18xx AudioDrive"); | ||
1833 | MODULE_LICENSE("GPL"); | ||
1834 | MODULE_SUPPORTED_DEVICE("{{ESS,ES1868 PnP AudioDrive}," | ||
1835 | "{ESS,ES1869 PnP AudioDrive}," | ||
1836 | "{ESS,ES1878 PnP AudioDrive}," | ||
1837 | "{ESS,ES1879 PnP AudioDrive}," | ||
1838 | "{ESS,ES1887 PnP AudioDrive}," | ||
1839 | "{ESS,ES1888 PnP AudioDrive}," | ||
1840 | "{ESS,ES1887 AudioDrive}," | ||
1841 | "{ESS,ES1888 AudioDrive}}"); | ||
1842 | |||
1843 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
1844 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
1845 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
1846 | #ifdef CONFIG_PNP | ||
1847 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
1848 | #endif | ||
1849 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ | ||
1850 | #ifndef CONFIG_PNP | ||
1851 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; | ||
1852 | #else | ||
1853 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
1854 | #endif | ||
1855 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
1856 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
1857 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
1858 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
1859 | |||
1860 | module_param_array(index, int, NULL, 0444); | ||
1861 | MODULE_PARM_DESC(index, "Index value for ES18xx soundcard."); | ||
1862 | module_param_array(id, charp, NULL, 0444); | ||
1863 | MODULE_PARM_DESC(id, "ID string for ES18xx soundcard."); | ||
1864 | module_param_array(enable, bool, NULL, 0444); | ||
1865 | MODULE_PARM_DESC(enable, "Enable ES18xx soundcard."); | ||
1866 | #ifdef CONFIG_PNP | ||
1867 | module_param_array(isapnp, bool, NULL, 0444); | ||
1868 | MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); | ||
1869 | #endif | ||
1870 | module_param_array(port, long, NULL, 0444); | ||
1871 | MODULE_PARM_DESC(port, "Port # for ES18xx driver."); | ||
1872 | module_param_array(mpu_port, long, NULL, 0444); | ||
1873 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver."); | ||
1874 | module_param_array(fm_port, long, NULL, 0444); | ||
1875 | MODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver."); | ||
1876 | module_param_array(irq, int, NULL, 0444); | ||
1877 | MODULE_PARM_DESC(irq, "IRQ # for ES18xx driver."); | ||
1878 | module_param_array(dma1, int, NULL, 0444); | ||
1879 | MODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver."); | ||
1880 | module_param_array(dma2, int, NULL, 0444); | ||
1881 | MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver."); | ||
1882 | |||
1883 | struct snd_audiodrive { | ||
1884 | #ifdef CONFIG_PNP | ||
1885 | struct pnp_dev *dev; | ||
1886 | struct pnp_dev *devc; | ||
1887 | #endif | ||
1888 | }; | ||
1889 | |||
1890 | static snd_card_t *snd_audiodrive_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
1891 | |||
1892 | #ifdef CONFIG_PNP | ||
1893 | |||
1894 | static struct pnp_card_device_id snd_audiodrive_pnpids[] = { | ||
1895 | /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ | ||
1896 | { .id = "ESS1868", .devs = { { "ESS1868" }, { "ESS0000" } } }, | ||
1897 | /* ESS 1868 (integrated on Maxisound Cards) */ | ||
1898 | { .id = "ESS1868", .devs = { { "ESS8601" }, { "ESS8600" } } }, | ||
1899 | /* ESS 1868 (integrated on Maxisound Cards) */ | ||
1900 | { .id = "ESS1868", .devs = { { "ESS8611" }, { "ESS8610" } } }, | ||
1901 | /* ESS ES1869 Plug and Play AudioDrive */ | ||
1902 | { .id = "ESS0003", .devs = { { "ESS1869" }, { "ESS0006" } } }, | ||
1903 | /* ESS 1869 */ | ||
1904 | { .id = "ESS1869", .devs = { { "ESS1869" }, { "ESS0006" } } }, | ||
1905 | /* ESS 1878 */ | ||
1906 | { .id = "ESS1878", .devs = { { "ESS1878" }, { "ESS0004" } } }, | ||
1907 | /* ESS 1879 */ | ||
1908 | { .id = "ESS1879", .devs = { { "ESS1879" }, { "ESS0009" } } }, | ||
1909 | /* --- */ | ||
1910 | { .id = "" } /* end */ | ||
1911 | }; | ||
1912 | |||
1913 | MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids); | ||
1914 | |||
1915 | static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard, | ||
1916 | struct pnp_card_link *card, | ||
1917 | const struct pnp_card_device_id *id) | ||
1918 | { | ||
1919 | struct pnp_dev *pdev; | ||
1920 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
1921 | int err; | ||
1922 | |||
1923 | if (!cfg) | ||
1924 | return -ENOMEM; | ||
1925 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
1926 | if (acard->dev == NULL) { | ||
1927 | kfree(cfg); | ||
1928 | return -EBUSY; | ||
1929 | } | ||
1930 | acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
1931 | if (acard->devc == NULL) { | ||
1932 | kfree(cfg); | ||
1933 | return -EBUSY; | ||
1934 | } | ||
1935 | /* Control port initialization */ | ||
1936 | err = pnp_activate_dev(acard->devc); | ||
1937 | if (err < 0) { | ||
1938 | snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n"); | ||
1939 | return -EAGAIN; | ||
1940 | } | ||
1941 | snd_printdd("pnp: port=0x%lx\n", pnp_port_start(acard->devc, 0)); | ||
1942 | /* PnP initialization */ | ||
1943 | pdev = acard->dev; | ||
1944 | pnp_init_resource_table(cfg); | ||
1945 | if (port[dev] != SNDRV_AUTO_PORT) | ||
1946 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
1947 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
1948 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
1949 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
1950 | pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2); | ||
1951 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
1952 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
1953 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
1954 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
1955 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
1956 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
1957 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
1958 | if (err < 0) | ||
1959 | snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n"); | ||
1960 | err = pnp_activate_dev(pdev); | ||
1961 | if (err < 0) { | ||
1962 | snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n"); | ||
1963 | kfree(cfg); | ||
1964 | return -EBUSY; | ||
1965 | } | ||
1966 | /* ok. hack using Vendor-Defined Card-Level registers */ | ||
1967 | /* skip csn and logdev initialization - already done in isapnp_configure */ | ||
1968 | if (pnp_device_is_isapnp(pdev)) { | ||
1969 | isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev)); | ||
1970 | isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */ | ||
1971 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
1972 | isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */ | ||
1973 | isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */ | ||
1974 | isapnp_cfg_end(); | ||
1975 | } else { | ||
1976 | snd_printk(KERN_ERR PFX "unable to install ISA PnP hack, expect malfunction\n"); | ||
1977 | } | ||
1978 | port[dev] = pnp_port_start(pdev, 0); | ||
1979 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
1980 | mpu_port[dev] = pnp_port_start(pdev, 2); | ||
1981 | dma1[dev] = pnp_dma(pdev, 0); | ||
1982 | dma2[dev] = pnp_dma(pdev, 1); | ||
1983 | irq[dev] = pnp_irq(pdev, 0); | ||
1984 | snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]); | ||
1985 | snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); | ||
1986 | kfree(cfg); | ||
1987 | return 0; | ||
1988 | } | ||
1989 | #endif /* CONFIG_PNP */ | ||
1990 | |||
1991 | static int __devinit snd_audiodrive_probe(int dev, struct pnp_card_link *pcard, | ||
1992 | const struct pnp_card_device_id *pid) | ||
1993 | { | ||
1994 | static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1}; | ||
1995 | static int possible_dmas[] = {1, 0, 3, 5, -1}; | ||
1996 | int xirq, xdma1, xdma2; | ||
1997 | snd_card_t *card; | ||
1998 | struct snd_audiodrive *acard; | ||
1999 | snd_rawmidi_t *rmidi = NULL; | ||
2000 | es18xx_t *chip; | ||
2001 | opl3_t *opl3; | ||
2002 | int err; | ||
2003 | |||
2004 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
2005 | sizeof(struct snd_audiodrive)); | ||
2006 | if (card == NULL) | ||
2007 | return -ENOMEM; | ||
2008 | acard = (struct snd_audiodrive *)card->private_data; | ||
2009 | #ifdef CONFIG_PNP | ||
2010 | if (isapnp[dev]) { | ||
2011 | if ((err = snd_audiodrive_pnp(dev, acard, pcard, pid)) < 0) { | ||
2012 | snd_card_free(card); | ||
2013 | return err; | ||
2014 | } | ||
2015 | snd_card_set_dev(card, &pcard->card->dev); | ||
2016 | } | ||
2017 | #endif | ||
2018 | |||
2019 | xirq = irq[dev]; | ||
2020 | if (xirq == SNDRV_AUTO_IRQ) { | ||
2021 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
2022 | snd_card_free(card); | ||
2023 | snd_printk("unable to find a free IRQ\n"); | ||
2024 | return -EBUSY; | ||
2025 | } | ||
2026 | } | ||
2027 | xdma1 = dma1[dev]; | ||
2028 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
2029 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
2030 | snd_card_free(card); | ||
2031 | snd_printk("unable to find a free DMA1\n"); | ||
2032 | return -EBUSY; | ||
2033 | } | ||
2034 | } | ||
2035 | xdma2 = dma2[dev]; | ||
2036 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
2037 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
2038 | snd_card_free(card); | ||
2039 | snd_printk("unable to find a free DMA2\n"); | ||
2040 | return -EBUSY; | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | if ((err = snd_es18xx_new_device(card, | ||
2045 | port[dev], | ||
2046 | mpu_port[dev], | ||
2047 | fm_port[dev], | ||
2048 | xirq, xdma1, xdma2, | ||
2049 | &chip)) < 0) { | ||
2050 | snd_card_free(card); | ||
2051 | return err; | ||
2052 | } | ||
2053 | |||
2054 | sprintf(card->driver, "ES%x", chip->version); | ||
2055 | sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version); | ||
2056 | if (xdma1 != xdma2) | ||
2057 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d", | ||
2058 | card->shortname, | ||
2059 | chip->port, | ||
2060 | xirq, xdma1, xdma2); | ||
2061 | else | ||
2062 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||
2063 | card->shortname, | ||
2064 | chip->port, | ||
2065 | xirq, xdma1); | ||
2066 | |||
2067 | if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0) { | ||
2068 | snd_card_free(card); | ||
2069 | return err; | ||
2070 | } | ||
2071 | if ((err = snd_es18xx_mixer(chip)) < 0) { | ||
2072 | snd_card_free(card); | ||
2073 | return err; | ||
2074 | } | ||
2075 | |||
2076 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
2077 | if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { | ||
2078 | snd_printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->fm_port); | ||
2079 | } else { | ||
2080 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
2081 | snd_card_free(card); | ||
2082 | return err; | ||
2083 | } | ||
2084 | } | ||
2085 | } | ||
2086 | |||
2087 | if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
2088 | if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, | ||
2089 | chip->mpu_port, 0, | ||
2090 | xirq, 0, | ||
2091 | &rmidi)) < 0) { | ||
2092 | snd_card_free(card); | ||
2093 | return err; | ||
2094 | } | ||
2095 | chip->rmidi = rmidi; | ||
2096 | } | ||
2097 | |||
2098 | /* Power Management */ | ||
2099 | snd_card_set_isa_pm_callback(card, snd_es18xx_suspend, snd_es18xx_resume, chip); | ||
2100 | |||
2101 | if ((err = snd_card_register(card)) < 0) { | ||
2102 | snd_card_free(card); | ||
2103 | return err; | ||
2104 | } | ||
2105 | if (pcard) | ||
2106 | pnp_set_card_drvdata(pcard, card); | ||
2107 | else | ||
2108 | snd_audiodrive_legacy[dev] = card; | ||
2109 | return 0; | ||
2110 | } | ||
2111 | |||
2112 | static int __devinit snd_audiodrive_probe_legacy_port(unsigned long xport) | ||
2113 | { | ||
2114 | static int dev; | ||
2115 | int res; | ||
2116 | |||
2117 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
2118 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
2119 | continue; | ||
2120 | #ifdef CONFIG_PNP | ||
2121 | if (isapnp[dev]) | ||
2122 | continue; | ||
2123 | #endif | ||
2124 | port[dev] = xport; | ||
2125 | res = snd_audiodrive_probe(dev, NULL, NULL); | ||
2126 | if (res < 0) | ||
2127 | port[dev] = SNDRV_AUTO_PORT; | ||
2128 | return res; | ||
2129 | } | ||
2130 | return -ENODEV; | ||
2131 | } | ||
2132 | |||
2133 | |||
2134 | #ifdef CONFIG_PNP | ||
2135 | static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *card, | ||
2136 | const struct pnp_card_device_id *id) | ||
2137 | { | ||
2138 | static int dev; | ||
2139 | int res; | ||
2140 | |||
2141 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
2142 | if (!enable[dev] || !isapnp[dev]) | ||
2143 | continue; | ||
2144 | res = snd_audiodrive_probe(dev, card, id); | ||
2145 | if (res < 0) | ||
2146 | return res; | ||
2147 | dev++; | ||
2148 | return 0; | ||
2149 | } | ||
2150 | |||
2151 | return -ENODEV; | ||
2152 | } | ||
2153 | |||
2154 | static void __devexit snd_audiodrive_pnp_remove(struct pnp_card_link * pcard) | ||
2155 | { | ||
2156 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
2157 | |||
2158 | snd_card_disconnect(card); | ||
2159 | snd_card_free_in_thread(card); | ||
2160 | } | ||
2161 | |||
2162 | static struct pnp_card_driver es18xx_pnpc_driver = { | ||
2163 | .flags = PNP_DRIVER_RES_DISABLE, | ||
2164 | .name = "es18xx", | ||
2165 | .id_table = snd_audiodrive_pnpids, | ||
2166 | .probe = snd_audiodrive_pnp_detect, | ||
2167 | .remove = __devexit_p(snd_audiodrive_pnp_remove), | ||
2168 | }; | ||
2169 | #endif /* CONFIG_PNP */ | ||
2170 | |||
2171 | static int __init alsa_card_es18xx_init(void) | ||
2172 | { | ||
2173 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; | ||
2174 | int dev, cards = 0, i; | ||
2175 | |||
2176 | /* legacy non-auto cards at first */ | ||
2177 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
2178 | if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) | ||
2179 | continue; | ||
2180 | #ifdef CONFIG_PNP | ||
2181 | if (isapnp[dev]) | ||
2182 | continue; | ||
2183 | #endif | ||
2184 | if (snd_audiodrive_probe(dev, NULL, NULL) >= 0) | ||
2185 | cards++; | ||
2186 | } | ||
2187 | /* legacy auto configured cards */ | ||
2188 | i = snd_legacy_auto_probe(possible_ports, snd_audiodrive_probe_legacy_port); | ||
2189 | if (i > 0) | ||
2190 | cards += i; | ||
2191 | |||
2192 | #ifdef CONFIG_PNP | ||
2193 | /* ISA PnP cards at last */ | ||
2194 | i = pnp_register_card_driver(&es18xx_pnpc_driver); | ||
2195 | if (i > 0) | ||
2196 | cards += i; | ||
2197 | |||
2198 | #endif | ||
2199 | if(!cards) { | ||
2200 | #ifdef CONFIG_PNP | ||
2201 | pnp_unregister_card_driver(&es18xx_pnpc_driver); | ||
2202 | #endif | ||
2203 | #ifdef MODULE | ||
2204 | snd_printk(KERN_ERR "ESS AudioDrive ES18xx soundcard not found or device busy\n"); | ||
2205 | #endif | ||
2206 | return -ENODEV; | ||
2207 | } | ||
2208 | return 0; | ||
2209 | } | ||
2210 | |||
2211 | static void __exit alsa_card_es18xx_exit(void) | ||
2212 | { | ||
2213 | int idx; | ||
2214 | |||
2215 | #ifdef CONFIG_PNP | ||
2216 | /* PnP cards first */ | ||
2217 | pnp_unregister_card_driver(&es18xx_pnpc_driver); | ||
2218 | #endif | ||
2219 | for(idx = 0; idx < SNDRV_CARDS; idx++) | ||
2220 | snd_card_free(snd_audiodrive_legacy[idx]); | ||
2221 | } | ||
2222 | |||
2223 | module_init(alsa_card_es18xx_init) | ||
2224 | module_exit(alsa_card_es18xx_exit) | ||
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile new file mode 100644 index 000000000000..bae5dbd6c8e5 --- /dev/null +++ b/sound/isa/gus/Makefile | |||
@@ -0,0 +1,36 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-gus-lib-objs := gus_main.o \ | ||
7 | gus_io.o gus_irq.o gus_timer.o \ | ||
8 | gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \ | ||
9 | gus_pcm.o gus_mixer.o \ | ||
10 | gus_uart.o \ | ||
11 | gus_reset.o | ||
12 | snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o | ||
13 | |||
14 | snd-gusclassic-objs := gusclassic.o | ||
15 | snd-gusextreme-objs := gusextreme.o | ||
16 | snd-gusmax-objs := gusmax.o | ||
17 | snd-interwave-objs := interwave.o | ||
18 | snd-interwave-stb-objs := interwave-stb.o | ||
19 | |||
20 | # | ||
21 | # this function returns: | ||
22 | # "m" - CONFIG_SND_SEQUENCER is m | ||
23 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
24 | # otherwise parameter #1 value | ||
25 | # | ||
26 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
27 | |||
28 | # Toplevel Module Dependency | ||
29 | obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o | ||
30 | obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o | ||
31 | obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o | ||
32 | obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o | ||
33 | obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o | ||
34 | obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o | ||
35 | |||
36 | obj-m := $(sort $(obj-m)) | ||
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c new file mode 100644 index 000000000000..de4b56d80b35 --- /dev/null +++ b/sound/isa/gus/gus_dma.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Routines for GF1 DMA control | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | |||
28 | static void snd_gf1_dma_ack(snd_gus_card_t * gus) | ||
29 | { | ||
30 | unsigned long flags; | ||
31 | |||
32 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
33 | snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00); | ||
34 | snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL); | ||
35 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
36 | } | ||
37 | |||
38 | static void snd_gf1_dma_program(snd_gus_card_t * gus, | ||
39 | unsigned int addr, | ||
40 | unsigned long buf_addr, | ||
41 | unsigned int count, | ||
42 | unsigned int cmd) | ||
43 | { | ||
44 | unsigned long flags; | ||
45 | unsigned int address; | ||
46 | unsigned char dma_cmd; | ||
47 | unsigned int address_high; | ||
48 | |||
49 | // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count); | ||
50 | |||
51 | if (gus->gf1.dma1 > 3) { | ||
52 | if (gus->gf1.enh_mode) { | ||
53 | address = addr >> 1; | ||
54 | } else { | ||
55 | if (addr & 0x1f) { | ||
56 | snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr); | ||
57 | return; | ||
58 | } | ||
59 | address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1); | ||
60 | } | ||
61 | } else { | ||
62 | address = addr; | ||
63 | } | ||
64 | |||
65 | dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd; | ||
66 | #if 0 | ||
67 | dma_cmd |= 0x08; | ||
68 | #endif | ||
69 | if (dma_cmd & SNDRV_GF1_DMA_16BIT) { | ||
70 | count++; | ||
71 | count &= ~1; /* align */ | ||
72 | } | ||
73 | if (gus->gf1.dma1 > 3) { | ||
74 | dma_cmd |= SNDRV_GF1_DMA_WIDTH16; | ||
75 | count++; | ||
76 | count &= ~1; /* align */ | ||
77 | } | ||
78 | snd_gf1_dma_ack(gus); | ||
79 | snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE); | ||
80 | #if 0 | ||
81 | snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd); | ||
82 | #endif | ||
83 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
84 | if (gus->gf1.enh_mode) { | ||
85 | address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f); | ||
86 | snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); | ||
87 | snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high); | ||
88 | } else | ||
89 | snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); | ||
90 | snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd); | ||
91 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
92 | } | ||
93 | |||
94 | static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus) | ||
95 | { | ||
96 | snd_gf1_dma_block_t *block; | ||
97 | |||
98 | /* PCM block have bigger priority than synthesizer one */ | ||
99 | if (gus->gf1.dma_data_pcm) { | ||
100 | block = gus->gf1.dma_data_pcm; | ||
101 | if (gus->gf1.dma_data_pcm_last == block) { | ||
102 | gus->gf1.dma_data_pcm = | ||
103 | gus->gf1.dma_data_pcm_last = NULL; | ||
104 | } else { | ||
105 | gus->gf1.dma_data_pcm = block->next; | ||
106 | } | ||
107 | } else if (gus->gf1.dma_data_synth) { | ||
108 | block = gus->gf1.dma_data_synth; | ||
109 | if (gus->gf1.dma_data_synth_last == block) { | ||
110 | gus->gf1.dma_data_synth = | ||
111 | gus->gf1.dma_data_synth_last = NULL; | ||
112 | } else { | ||
113 | gus->gf1.dma_data_synth = block->next; | ||
114 | } | ||
115 | } else { | ||
116 | block = NULL; | ||
117 | } | ||
118 | if (block) { | ||
119 | gus->gf1.dma_ack = block->ack; | ||
120 | gus->gf1.dma_private_data = block->private_data; | ||
121 | } | ||
122 | return block; | ||
123 | } | ||
124 | |||
125 | |||
126 | static void snd_gf1_dma_interrupt(snd_gus_card_t * gus) | ||
127 | { | ||
128 | snd_gf1_dma_block_t *block; | ||
129 | |||
130 | snd_gf1_dma_ack(gus); | ||
131 | if (gus->gf1.dma_ack) | ||
132 | gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); | ||
133 | spin_lock(&gus->dma_lock); | ||
134 | if (gus->gf1.dma_data_pcm == NULL && | ||
135 | gus->gf1.dma_data_synth == NULL) { | ||
136 | gus->gf1.dma_ack = NULL; | ||
137 | gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; | ||
138 | spin_unlock(&gus->dma_lock); | ||
139 | return; | ||
140 | } | ||
141 | block = snd_gf1_dma_next_block(gus); | ||
142 | spin_unlock(&gus->dma_lock); | ||
143 | snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); | ||
144 | kfree(block); | ||
145 | #if 0 | ||
146 | printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd); | ||
147 | #endif | ||
148 | } | ||
149 | |||
150 | int snd_gf1_dma_init(snd_gus_card_t * gus) | ||
151 | { | ||
152 | down(&gus->dma_mutex); | ||
153 | gus->gf1.dma_shared++; | ||
154 | if (gus->gf1.dma_shared > 1) { | ||
155 | up(&gus->dma_mutex); | ||
156 | return 0; | ||
157 | } | ||
158 | gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt; | ||
159 | gus->gf1.dma_data_pcm = | ||
160 | gus->gf1.dma_data_pcm_last = | ||
161 | gus->gf1.dma_data_synth = | ||
162 | gus->gf1.dma_data_synth_last = NULL; | ||
163 | up(&gus->dma_mutex); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | int snd_gf1_dma_done(snd_gus_card_t * gus) | ||
168 | { | ||
169 | snd_gf1_dma_block_t *block; | ||
170 | |||
171 | down(&gus->dma_mutex); | ||
172 | gus->gf1.dma_shared--; | ||
173 | if (!gus->gf1.dma_shared) { | ||
174 | snd_dma_disable(gus->gf1.dma1); | ||
175 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE); | ||
176 | snd_gf1_dma_ack(gus); | ||
177 | while ((block = gus->gf1.dma_data_pcm)) { | ||
178 | gus->gf1.dma_data_pcm = block->next; | ||
179 | kfree(block); | ||
180 | } | ||
181 | while ((block = gus->gf1.dma_data_synth)) { | ||
182 | gus->gf1.dma_data_synth = block->next; | ||
183 | kfree(block); | ||
184 | } | ||
185 | gus->gf1.dma_data_pcm_last = | ||
186 | gus->gf1.dma_data_synth_last = NULL; | ||
187 | } | ||
188 | up(&gus->dma_mutex); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, | ||
193 | snd_gf1_dma_block_t * __block, | ||
194 | int atomic, | ||
195 | int synth) | ||
196 | { | ||
197 | unsigned long flags; | ||
198 | snd_gf1_dma_block_t *block; | ||
199 | |||
200 | block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL); | ||
201 | if (block == NULL) { | ||
202 | snd_printk("gf1: DMA transfer failure; not enough memory\n"); | ||
203 | return -ENOMEM; | ||
204 | } | ||
205 | *block = *__block; | ||
206 | block->next = NULL; | ||
207 | #if 0 | ||
208 | printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd); | ||
209 | #endif | ||
210 | #if 0 | ||
211 | printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last); | ||
212 | printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm); | ||
213 | #endif | ||
214 | spin_lock_irqsave(&gus->dma_lock, flags); | ||
215 | if (synth) { | ||
216 | if (gus->gf1.dma_data_synth_last) { | ||
217 | gus->gf1.dma_data_synth_last->next = block; | ||
218 | gus->gf1.dma_data_synth_last = block; | ||
219 | } else { | ||
220 | gus->gf1.dma_data_synth = | ||
221 | gus->gf1.dma_data_synth_last = block; | ||
222 | } | ||
223 | } else { | ||
224 | if (gus->gf1.dma_data_pcm_last) { | ||
225 | gus->gf1.dma_data_pcm_last->next = block; | ||
226 | gus->gf1.dma_data_pcm_last = block; | ||
227 | } else { | ||
228 | gus->gf1.dma_data_pcm = | ||
229 | gus->gf1.dma_data_pcm_last = block; | ||
230 | } | ||
231 | } | ||
232 | if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) { | ||
233 | gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER; | ||
234 | block = snd_gf1_dma_next_block(gus); | ||
235 | spin_unlock_irqrestore(&gus->dma_lock, flags); | ||
236 | if (block == NULL) | ||
237 | return 0; | ||
238 | snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); | ||
239 | kfree(block); | ||
240 | return 0; | ||
241 | } | ||
242 | spin_unlock_irqrestore(&gus->dma_lock, flags); | ||
243 | return 0; | ||
244 | } | ||
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c new file mode 100644 index 000000000000..22120b868b5c --- /dev/null +++ b/sound/isa/gus/gus_dram.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * DRAM access routines | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include <sound/info.h> | ||
27 | |||
28 | |||
29 | static int snd_gus_dram_poke(snd_gus_card_t *gus, char __user *_buffer, | ||
30 | unsigned int address, unsigned int size) | ||
31 | { | ||
32 | unsigned long flags; | ||
33 | unsigned int size1, size2; | ||
34 | char buffer[256], *pbuffer; | ||
35 | |||
36 | while (size > 0) { | ||
37 | size1 = size > sizeof(buffer) ? sizeof(buffer) : size; | ||
38 | if (copy_from_user(buffer, _buffer, size1)) | ||
39 | return -EFAULT; | ||
40 | if (gus->interwave) { | ||
41 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
42 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
43 | snd_gf1_dram_addr(gus, address); | ||
44 | outsb(GUSP(gus, DRAM), buffer, size1); | ||
45 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
46 | address += size1; | ||
47 | } else { | ||
48 | pbuffer = buffer; | ||
49 | size2 = size1; | ||
50 | while (size2--) | ||
51 | snd_gf1_poke(gus, address++, *pbuffer++); | ||
52 | } | ||
53 | size -= size1; | ||
54 | _buffer += size1; | ||
55 | } | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | |||
60 | int snd_gus_dram_write(snd_gus_card_t *gus, char __user *buffer, | ||
61 | unsigned int address, unsigned int size) | ||
62 | { | ||
63 | return snd_gus_dram_poke(gus, buffer, address, size); | ||
64 | } | ||
65 | |||
66 | static int snd_gus_dram_peek(snd_gus_card_t *gus, char __user *_buffer, | ||
67 | unsigned int address, unsigned int size, | ||
68 | int rom) | ||
69 | { | ||
70 | unsigned long flags; | ||
71 | unsigned int size1, size2; | ||
72 | char buffer[256], *pbuffer; | ||
73 | |||
74 | while (size > 0) { | ||
75 | size1 = size > sizeof(buffer) ? sizeof(buffer) : size; | ||
76 | if (gus->interwave) { | ||
77 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
78 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01); | ||
79 | snd_gf1_dram_addr(gus, address); | ||
80 | insb(GUSP(gus, DRAM), buffer, size1); | ||
81 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
82 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
83 | address += size1; | ||
84 | } else { | ||
85 | pbuffer = buffer; | ||
86 | size2 = size1; | ||
87 | while (size2--) | ||
88 | *pbuffer++ = snd_gf1_peek(gus, address++); | ||
89 | } | ||
90 | if (copy_to_user(_buffer, buffer, size1)) | ||
91 | return -EFAULT; | ||
92 | size -= size1; | ||
93 | _buffer += size1; | ||
94 | } | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | int snd_gus_dram_read(snd_gus_card_t *gus, char __user *buffer, | ||
99 | unsigned int address, unsigned int size, | ||
100 | int rom) | ||
101 | { | ||
102 | return snd_gus_dram_peek(gus, buffer, address, size, rom); | ||
103 | } | ||
diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c new file mode 100644 index 000000000000..591a9a17feb5 --- /dev/null +++ b/sound/isa/gus/gus_instr.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Synthesizer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | |||
27 | /* | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, | ||
32 | char __user *data, long len, int atomic) | ||
33 | { | ||
34 | snd_gus_card_t *gus = private_data; | ||
35 | snd_gf1_mem_block_t *block; | ||
36 | int err; | ||
37 | |||
38 | if (wave->format & IWFFFF_WAVE_ROM) | ||
39 | return 0; /* it's probably ok - verify the address? */ | ||
40 | if (wave->format & IWFFFF_WAVE_STEREO) | ||
41 | return -EINVAL; /* not supported */ | ||
42 | block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
43 | SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF, | ||
44 | NULL, wave->size, | ||
45 | wave->format & IWFFFF_WAVE_16BIT, 1, | ||
46 | wave->share_id); | ||
47 | if (block == NULL) | ||
48 | return -ENOMEM; | ||
49 | err = snd_gus_dram_write(gus, data, | ||
50 | block->ptr, wave->size); | ||
51 | if (err < 0) { | ||
52 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); | ||
53 | snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); | ||
54 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); | ||
55 | return err; | ||
56 | } | ||
57 | wave->address.memory = block->ptr; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, | ||
62 | char __user *data, long len, int atomic) | ||
63 | { | ||
64 | snd_gus_card_t *gus = private_data; | ||
65 | |||
66 | return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, | ||
67 | wave->format & IWFFFF_WAVE_ROM ? 1 : 0); | ||
68 | } | ||
69 | |||
70 | int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, | ||
71 | int atomic) | ||
72 | { | ||
73 | snd_gus_card_t *gus = private_data; | ||
74 | |||
75 | if (wave->format & IWFFFF_WAVE_ROM) | ||
76 | return 0; /* it's probably ok - verify the address? */ | ||
77 | return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * | ||
82 | */ | ||
83 | |||
84 | int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, | ||
85 | char __user *data, long len, int atomic) | ||
86 | { | ||
87 | snd_gus_card_t *gus = private_data; | ||
88 | snd_gf1_mem_block_t *block; | ||
89 | int err; | ||
90 | |||
91 | if (wave->format & GF1_WAVE_STEREO) | ||
92 | return -EINVAL; /* not supported */ | ||
93 | block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
94 | SNDRV_GF1_MEM_OWNER_WAVE_GF1, | ||
95 | NULL, wave->size, | ||
96 | wave->format & GF1_WAVE_16BIT, 1, | ||
97 | wave->share_id); | ||
98 | if (block == NULL) | ||
99 | return -ENOMEM; | ||
100 | err = snd_gus_dram_write(gus, data, | ||
101 | block->ptr, wave->size); | ||
102 | if (err < 0) { | ||
103 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); | ||
104 | snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); | ||
105 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); | ||
106 | return err; | ||
107 | } | ||
108 | wave->address.memory = block->ptr; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, | ||
113 | char __user *data, long len, int atomic) | ||
114 | { | ||
115 | snd_gus_card_t *gus = private_data; | ||
116 | |||
117 | return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); | ||
118 | } | ||
119 | |||
120 | int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, | ||
121 | int atomic) | ||
122 | { | ||
123 | snd_gus_card_t *gus = private_data; | ||
124 | |||
125 | return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * | ||
130 | */ | ||
131 | |||
132 | int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, | ||
133 | char __user *data, long len, int atomic) | ||
134 | { | ||
135 | snd_gus_card_t *gus = private_data; | ||
136 | snd_gf1_mem_block_t *block; | ||
137 | int err; | ||
138 | |||
139 | if (instr->format & SIMPLE_WAVE_STEREO) | ||
140 | return -EINVAL; /* not supported */ | ||
141 | block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
142 | SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE, | ||
143 | NULL, instr->size, | ||
144 | instr->format & SIMPLE_WAVE_16BIT, 1, | ||
145 | instr->share_id); | ||
146 | if (block == NULL) | ||
147 | return -ENOMEM; | ||
148 | err = snd_gus_dram_write(gus, data, block->ptr, instr->size); | ||
149 | if (err < 0) { | ||
150 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); | ||
151 | snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); | ||
152 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); | ||
153 | return err; | ||
154 | } | ||
155 | instr->address.memory = block->ptr; | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, | ||
160 | char __user *data, long len, int atomic) | ||
161 | { | ||
162 | snd_gus_card_t *gus = private_data; | ||
163 | |||
164 | return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); | ||
165 | } | ||
166 | |||
167 | int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, | ||
168 | int atomic) | ||
169 | { | ||
170 | snd_gus_card_t *gus = private_data; | ||
171 | |||
172 | return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); | ||
173 | } | ||
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c new file mode 100644 index 000000000000..f0570f2bf75f --- /dev/null +++ b/sound/isa/gus/gus_io.c | |||
@@ -0,0 +1,531 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * I/O routines for GF1/InterWave synthesizer chips | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | |||
28 | void snd_gf1_delay(snd_gus_card_t * gus) | ||
29 | { | ||
30 | int i; | ||
31 | |||
32 | for (i = 0; i < 6; i++) { | ||
33 | mb(); | ||
34 | inb(GUSP(gus, DRAM)); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * ======================================================================= | ||
40 | */ | ||
41 | |||
42 | /* | ||
43 | * ok.. stop of control registers (wave & ramp) need some special things.. | ||
44 | * big UltraClick (tm) elimination... | ||
45 | */ | ||
46 | |||
47 | static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) | ||
48 | { | ||
49 | unsigned char value; | ||
50 | |||
51 | outb(reg | 0x80, gus->gf1.reg_regsel); | ||
52 | mb(); | ||
53 | value = inb(gus->gf1.reg_data8); | ||
54 | mb(); | ||
55 | outb(reg, gus->gf1.reg_regsel); | ||
56 | mb(); | ||
57 | outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8); | ||
58 | mb(); | ||
59 | } | ||
60 | |||
61 | static inline void __snd_gf1_write8(snd_gus_card_t * gus, | ||
62 | unsigned char reg, | ||
63 | unsigned char data) | ||
64 | { | ||
65 | outb(reg, gus->gf1.reg_regsel); | ||
66 | mb(); | ||
67 | outb(data, gus->gf1.reg_data8); | ||
68 | mb(); | ||
69 | } | ||
70 | |||
71 | static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus, | ||
72 | unsigned char reg) | ||
73 | { | ||
74 | outb(reg, gus->gf1.reg_regsel); | ||
75 | mb(); | ||
76 | return inb(gus->gf1.reg_data8); | ||
77 | } | ||
78 | |||
79 | static inline void __snd_gf1_write16(snd_gus_card_t * gus, | ||
80 | unsigned char reg, unsigned int data) | ||
81 | { | ||
82 | outb(reg, gus->gf1.reg_regsel); | ||
83 | mb(); | ||
84 | outw((unsigned short) data, gus->gf1.reg_data16); | ||
85 | mb(); | ||
86 | } | ||
87 | |||
88 | static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus, | ||
89 | unsigned char reg) | ||
90 | { | ||
91 | outb(reg, gus->gf1.reg_regsel); | ||
92 | mb(); | ||
93 | return inw(gus->gf1.reg_data16); | ||
94 | } | ||
95 | |||
96 | static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus, | ||
97 | unsigned char reg, unsigned char data) | ||
98 | { | ||
99 | outb(reg, gus->gf1.reg_timerctrl); | ||
100 | inb(gus->gf1.reg_timerctrl); | ||
101 | inb(gus->gf1.reg_timerctrl); | ||
102 | outb(data, gus->gf1.reg_timerdata); | ||
103 | inb(gus->gf1.reg_timerctrl); | ||
104 | inb(gus->gf1.reg_timerctrl); | ||
105 | } | ||
106 | |||
107 | static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, | ||
108 | unsigned int addr, int w_16bit) | ||
109 | { | ||
110 | if (gus->gf1.enh_mode) { | ||
111 | if (w_16bit) | ||
112 | addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f); | ||
113 | __snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03)); | ||
114 | } else if (w_16bit) | ||
115 | addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1); | ||
116 | __snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11)); | ||
117 | __snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5)); | ||
118 | } | ||
119 | |||
120 | static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus, | ||
121 | unsigned char reg, short w_16bit) | ||
122 | { | ||
123 | unsigned int res; | ||
124 | |||
125 | res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800; | ||
126 | res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff; | ||
127 | if (gus->gf1.enh_mode) { | ||
128 | res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26; | ||
129 | if (w_16bit) | ||
130 | res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f); | ||
131 | } else if (w_16bit) | ||
132 | res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f); | ||
133 | return res; | ||
134 | } | ||
135 | |||
136 | |||
137 | /* | ||
138 | * ======================================================================= | ||
139 | */ | ||
140 | |||
141 | void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) | ||
142 | { | ||
143 | __snd_gf1_ctrl_stop(gus, reg); | ||
144 | } | ||
145 | |||
146 | void snd_gf1_write8(snd_gus_card_t * gus, | ||
147 | unsigned char reg, | ||
148 | unsigned char data) | ||
149 | { | ||
150 | __snd_gf1_write8(gus, reg, data); | ||
151 | } | ||
152 | |||
153 | unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg) | ||
154 | { | ||
155 | return __snd_gf1_look8(gus, reg); | ||
156 | } | ||
157 | |||
158 | void snd_gf1_write16(snd_gus_card_t * gus, | ||
159 | unsigned char reg, | ||
160 | unsigned int data) | ||
161 | { | ||
162 | __snd_gf1_write16(gus, reg, data); | ||
163 | } | ||
164 | |||
165 | unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg) | ||
166 | { | ||
167 | return __snd_gf1_look16(gus, reg); | ||
168 | } | ||
169 | |||
170 | void snd_gf1_adlib_write(snd_gus_card_t * gus, | ||
171 | unsigned char reg, | ||
172 | unsigned char data) | ||
173 | { | ||
174 | __snd_gf1_adlib_write(gus, reg, data); | ||
175 | } | ||
176 | |||
177 | void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, | ||
178 | unsigned int addr, short w_16bit) | ||
179 | { | ||
180 | __snd_gf1_write_addr(gus, reg, addr, w_16bit); | ||
181 | } | ||
182 | |||
183 | unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, | ||
184 | unsigned char reg, | ||
185 | short w_16bit) | ||
186 | { | ||
187 | return __snd_gf1_read_addr(gus, reg, w_16bit); | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | |||
192 | */ | ||
193 | |||
194 | void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) | ||
195 | { | ||
196 | unsigned long flags; | ||
197 | |||
198 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
199 | __snd_gf1_ctrl_stop(gus, reg); | ||
200 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
201 | } | ||
202 | |||
203 | void snd_gf1_i_write8(snd_gus_card_t * gus, | ||
204 | unsigned char reg, | ||
205 | unsigned char data) | ||
206 | { | ||
207 | unsigned long flags; | ||
208 | |||
209 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
210 | __snd_gf1_write8(gus, reg, data); | ||
211 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
212 | } | ||
213 | |||
214 | unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg) | ||
215 | { | ||
216 | unsigned long flags; | ||
217 | unsigned char res; | ||
218 | |||
219 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
220 | res = __snd_gf1_look8(gus, reg); | ||
221 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
222 | return res; | ||
223 | } | ||
224 | |||
225 | void snd_gf1_i_write16(snd_gus_card_t * gus, | ||
226 | unsigned char reg, | ||
227 | unsigned int data) | ||
228 | { | ||
229 | unsigned long flags; | ||
230 | |||
231 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
232 | __snd_gf1_write16(gus, reg, data); | ||
233 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
234 | } | ||
235 | |||
236 | unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg) | ||
237 | { | ||
238 | unsigned long flags; | ||
239 | unsigned short res; | ||
240 | |||
241 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
242 | res = __snd_gf1_look16(gus, reg); | ||
243 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
244 | return res; | ||
245 | } | ||
246 | |||
247 | void snd_gf1_i_adlib_write(snd_gus_card_t * gus, | ||
248 | unsigned char reg, | ||
249 | unsigned char data) | ||
250 | { | ||
251 | unsigned long flags; | ||
252 | |||
253 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
254 | __snd_gf1_adlib_write(gus, reg, data); | ||
255 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
256 | } | ||
257 | |||
258 | void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, | ||
259 | unsigned int addr, short w_16bit) | ||
260 | { | ||
261 | unsigned long flags; | ||
262 | |||
263 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
264 | __snd_gf1_write_addr(gus, reg, addr, w_16bit); | ||
265 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
266 | } | ||
267 | |||
268 | unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, | ||
269 | unsigned char reg, short w_16bit) | ||
270 | { | ||
271 | unsigned int res; | ||
272 | unsigned long flags; | ||
273 | |||
274 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
275 | res = __snd_gf1_read_addr(gus, reg, w_16bit); | ||
276 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
277 | return res; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | |||
282 | */ | ||
283 | |||
284 | void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr) | ||
285 | { | ||
286 | outb(0x43, gus->gf1.reg_regsel); | ||
287 | mb(); | ||
288 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
289 | mb(); | ||
290 | outb(0x44, gus->gf1.reg_regsel); | ||
291 | mb(); | ||
292 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
293 | mb(); | ||
294 | } | ||
295 | |||
296 | void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data) | ||
297 | { | ||
298 | unsigned long flags; | ||
299 | |||
300 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
301 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
302 | mb(); | ||
303 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
304 | mb(); | ||
305 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
306 | mb(); | ||
307 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
308 | mb(); | ||
309 | outb(data, gus->gf1.reg_dram); | ||
310 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
311 | } | ||
312 | |||
313 | unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr) | ||
314 | { | ||
315 | unsigned long flags; | ||
316 | unsigned char res; | ||
317 | |||
318 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
319 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
320 | mb(); | ||
321 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
322 | mb(); | ||
323 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
324 | mb(); | ||
325 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
326 | mb(); | ||
327 | res = inb(gus->gf1.reg_dram); | ||
328 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
329 | return res; | ||
330 | } | ||
331 | |||
332 | void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data) | ||
333 | { | ||
334 | unsigned long flags; | ||
335 | |||
336 | #ifdef CONFIG_SND_DEBUG | ||
337 | if (!gus->interwave) | ||
338 | snd_printk("snd_gf1_pokew - GF1!!!\n"); | ||
339 | #endif | ||
340 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
341 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
342 | mb(); | ||
343 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
344 | mb(); | ||
345 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
346 | mb(); | ||
347 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
348 | mb(); | ||
349 | outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); | ||
350 | mb(); | ||
351 | outw(data, gus->gf1.reg_data16); | ||
352 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
353 | } | ||
354 | |||
355 | unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr) | ||
356 | { | ||
357 | unsigned long flags; | ||
358 | unsigned short res; | ||
359 | |||
360 | #ifdef CONFIG_SND_DEBUG | ||
361 | if (!gus->interwave) | ||
362 | snd_printk("snd_gf1_peekw - GF1!!!\n"); | ||
363 | #endif | ||
364 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
365 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
366 | mb(); | ||
367 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
368 | mb(); | ||
369 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
370 | mb(); | ||
371 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
372 | mb(); | ||
373 | outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); | ||
374 | mb(); | ||
375 | res = inw(gus->gf1.reg_data16); | ||
376 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
377 | return res; | ||
378 | } | ||
379 | |||
380 | void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, | ||
381 | unsigned short value, unsigned int count) | ||
382 | { | ||
383 | unsigned long port; | ||
384 | unsigned long flags; | ||
385 | |||
386 | #ifdef CONFIG_SND_DEBUG | ||
387 | if (!gus->interwave) | ||
388 | snd_printk("snd_gf1_dram_setmem - GF1!!!\n"); | ||
389 | #endif | ||
390 | addr &= ~1; | ||
391 | count >>= 1; | ||
392 | port = GUSP(gus, GF1DATALOW); | ||
393 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
394 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
395 | mb(); | ||
396 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
397 | mb(); | ||
398 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
399 | mb(); | ||
400 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
401 | mb(); | ||
402 | outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); | ||
403 | while (count--) | ||
404 | outw(value, port); | ||
405 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | |||
410 | */ | ||
411 | |||
412 | void snd_gf1_select_active_voices(snd_gus_card_t * gus) | ||
413 | { | ||
414 | unsigned short voices; | ||
415 | |||
416 | static unsigned short voices_tbl[32 - 14 + 1] = | ||
417 | { | ||
418 | 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, | ||
419 | 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 | ||
420 | }; | ||
421 | |||
422 | voices = gus->gf1.active_voices; | ||
423 | if (voices > 32) | ||
424 | voices = 32; | ||
425 | if (voices < 14) | ||
426 | voices = 14; | ||
427 | if (gus->gf1.enh_mode) | ||
428 | voices = 32; | ||
429 | gus->gf1.active_voices = voices; | ||
430 | gus->gf1.playback_freq = | ||
431 | gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14]; | ||
432 | if (!gus->gf1.enh_mode) { | ||
433 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1)); | ||
434 | udelay(100); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | #ifdef CONFIG_SND_DEBUG | ||
439 | |||
440 | void snd_gf1_print_voice_registers(snd_gus_card_t * gus) | ||
441 | { | ||
442 | unsigned char mode; | ||
443 | int voice, ctrl; | ||
444 | |||
445 | voice = gus->gf1.active_voice; | ||
446 | printk(" -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d)); | ||
447 | printk(" -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1)); | ||
448 | printk(" -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4)); | ||
449 | printk(" -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6)); | ||
450 | printk(" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9)); | ||
451 | printk(" -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4)); | ||
452 | if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */ | ||
453 | mode = snd_gf1_i_read8(gus, 0x15); | ||
454 | printk(" -%i- GFA1 mode = 0x%x\n", voice, mode); | ||
455 | if (mode & 0x01) { /* Effect processor */ | ||
456 | printk(" -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4)); | ||
457 | printk(" -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16)); | ||
458 | printk(" -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d)); | ||
459 | printk(" -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14)); | ||
460 | } | ||
461 | if (mode & 0x20) { | ||
462 | printk(" -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4); | ||
463 | printk(" -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4); | ||
464 | printk(" -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4); | ||
465 | printk(" -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4); | ||
466 | } else | ||
467 | printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); | ||
468 | } else | ||
469 | printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); | ||
470 | } | ||
471 | |||
472 | void snd_gf1_print_global_registers(snd_gus_card_t * gus) | ||
473 | { | ||
474 | unsigned char global_mode = 0x00; | ||
475 | |||
476 | printk(" -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES)); | ||
477 | if (gus->interwave) { | ||
478 | global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE); | ||
479 | printk(" -G- GF1 global mode = 0x%x\n", global_mode); | ||
480 | } | ||
481 | if (global_mode & 0x02) /* LFO enabled? */ | ||
482 | printk(" -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE)); | ||
483 | printk(" -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ)); | ||
484 | printk(" -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL)); | ||
485 | printk(" -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW)); | ||
486 | printk(" -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW)); | ||
487 | if (!gus->interwave) | ||
488 | printk(" -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL)); | ||
489 | printk(" -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16)); | ||
490 | if (gus->gf1.enh_mode) { | ||
491 | printk(" -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG)); | ||
492 | printk(" -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL)); | ||
493 | printk(" -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR)); | ||
494 | printk(" -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR)); | ||
495 | printk(" -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE)); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | void snd_gf1_print_setup_registers(snd_gus_card_t * gus) | ||
500 | { | ||
501 | printk(" -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG))); | ||
502 | printk(" -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT))); | ||
503 | printk(" -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL))); | ||
504 | printk(" -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA))); | ||
505 | printk(" -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS))); | ||
506 | printk(" -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL)); | ||
507 | printk(" -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2)); | ||
508 | printk(" -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)); | ||
509 | if (gus->interwave) { | ||
510 | printk(" -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY)); | ||
511 | printk(" -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL)); | ||
512 | printk(" -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER)); | ||
513 | printk(" -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B)); | ||
514 | printk(" -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ)); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit) | ||
519 | { | ||
520 | if (!w_16bit) { | ||
521 | while (count-- > 0) | ||
522 | printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++)); | ||
523 | } else { | ||
524 | while (count-- > 0) { | ||
525 | printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8)); | ||
526 | addr += 2; | ||
527 | } | ||
528 | } | ||
529 | } | ||
530 | |||
531 | #endif | ||
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c new file mode 100644 index 000000000000..1e2a15eb8106 --- /dev/null +++ b/sound/isa/gus/gus_irq.c | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * Routine for IRQ handling from GF1/InterWave chip | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/info.h> | ||
25 | #include <sound/gus.h> | ||
26 | |||
27 | #ifdef CONFIG_SND_DEBUG | ||
28 | #define STAT_ADD(x) ((x)++) | ||
29 | #else | ||
30 | #define STAT_ADD(x) while (0) { ; } | ||
31 | #endif | ||
32 | |||
33 | irqreturn_t snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
34 | { | ||
35 | snd_gus_card_t * gus = dev_id; | ||
36 | unsigned char status; | ||
37 | int loop = 100; | ||
38 | int handled = 0; | ||
39 | |||
40 | __again: | ||
41 | status = inb(gus->gf1.reg_irqstat); | ||
42 | if (status == 0) | ||
43 | return IRQ_RETVAL(handled); | ||
44 | handled = 1; | ||
45 | // snd_printk("IRQ: status = 0x%x\n", status); | ||
46 | if (status & 0x02) { | ||
47 | STAT_ADD(gus->gf1.interrupt_stat_midi_in); | ||
48 | gus->gf1.interrupt_handler_midi_in(gus); | ||
49 | } | ||
50 | if (status & 0x01) { | ||
51 | STAT_ADD(gus->gf1.interrupt_stat_midi_out); | ||
52 | gus->gf1.interrupt_handler_midi_out(gus); | ||
53 | } | ||
54 | if (status & (0x20 | 0x40)) { | ||
55 | unsigned int already, _current_; | ||
56 | unsigned char voice_status, voice; | ||
57 | snd_gus_voice_t *pvoice; | ||
58 | |||
59 | already = 0; | ||
60 | while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) { | ||
61 | voice = voice_status & 0x1f; | ||
62 | _current_ = 1 << voice; | ||
63 | if (already & _current_) | ||
64 | continue; /* multi request */ | ||
65 | already |= _current_; /* mark request */ | ||
66 | #if 0 | ||
67 | printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE))); | ||
68 | #endif | ||
69 | pvoice = &gus->gf1.voices[voice]; | ||
70 | if (pvoice->use) { | ||
71 | if (!(voice_status & 0x80)) { /* voice position IRQ */ | ||
72 | STAT_ADD(pvoice->interrupt_stat_wave); | ||
73 | pvoice->handler_wave(gus, pvoice); | ||
74 | } | ||
75 | if (!(voice_status & 0x40)) { /* volume ramp IRQ */ | ||
76 | STAT_ADD(pvoice->interrupt_stat_volume); | ||
77 | pvoice->handler_volume(gus, pvoice); | ||
78 | } | ||
79 | } else { | ||
80 | STAT_ADD(gus->gf1.interrupt_stat_voice_lost); | ||
81 | snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
82 | snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | if (status & 0x04) { | ||
87 | STAT_ADD(gus->gf1.interrupt_stat_timer1); | ||
88 | gus->gf1.interrupt_handler_timer1(gus); | ||
89 | } | ||
90 | if (status & 0x08) { | ||
91 | STAT_ADD(gus->gf1.interrupt_stat_timer2); | ||
92 | gus->gf1.interrupt_handler_timer2(gus); | ||
93 | } | ||
94 | if (status & 0x80) { | ||
95 | if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) { | ||
96 | STAT_ADD(gus->gf1.interrupt_stat_dma_write); | ||
97 | gus->gf1.interrupt_handler_dma_write(gus); | ||
98 | } | ||
99 | if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) { | ||
100 | STAT_ADD(gus->gf1.interrupt_stat_dma_read); | ||
101 | gus->gf1.interrupt_handler_dma_read(gus); | ||
102 | } | ||
103 | } | ||
104 | if (--loop > 0) | ||
105 | goto __again; | ||
106 | return IRQ_NONE; | ||
107 | } | ||
108 | |||
109 | #ifdef CONFIG_SND_DEBUG | ||
110 | static void snd_gus_irq_info_read(snd_info_entry_t *entry, | ||
111 | snd_info_buffer_t * buffer) | ||
112 | { | ||
113 | snd_gus_card_t *gus; | ||
114 | snd_gus_voice_t *pvoice; | ||
115 | int idx; | ||
116 | |||
117 | gus = entry->private_data; | ||
118 | snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out); | ||
119 | snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in); | ||
120 | snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1); | ||
121 | snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2); | ||
122 | snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write); | ||
123 | snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read); | ||
124 | snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost); | ||
125 | for (idx = 0; idx < 32; idx++) { | ||
126 | pvoice = &gus->gf1.voices[idx]; | ||
127 | snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n", | ||
128 | idx, | ||
129 | pvoice->interrupt_stat_wave, | ||
130 | pvoice->interrupt_stat_volume); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | void snd_gus_irq_profile_init(snd_gus_card_t *gus) | ||
135 | { | ||
136 | snd_info_entry_t *entry; | ||
137 | |||
138 | if (! snd_card_proc_new(gus->card, "gusirq", &entry)) | ||
139 | snd_info_set_text_ops(entry, gus, 1024, snd_gus_irq_info_read); | ||
140 | } | ||
141 | |||
142 | #endif | ||
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c new file mode 100644 index 000000000000..73f81c14f768 --- /dev/null +++ b/sound/isa/gus/gus_main.c | |||
@@ -0,0 +1,514 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #include <sound/control.h> | ||
31 | |||
32 | #include <asm/dma.h> | ||
33 | |||
34 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
35 | MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches); | ||
39 | |||
40 | int snd_gus_use_inc(snd_gus_card_t * gus) | ||
41 | { | ||
42 | if (!try_module_get(gus->card->module)) | ||
43 | return 0; | ||
44 | return 1; | ||
45 | } | ||
46 | |||
47 | void snd_gus_use_dec(snd_gus_card_t * gus) | ||
48 | { | ||
49 | module_put(gus->card->module); | ||
50 | } | ||
51 | |||
52 | static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
53 | { | ||
54 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
55 | uinfo->count = 1; | ||
56 | uinfo->value.integer.min = 0; | ||
57 | uinfo->value.integer.max = 31; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
62 | { | ||
63 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
64 | |||
65 | ucontrol->value.integer.value[0] = gus->joystick_dac & 31; | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
70 | { | ||
71 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
72 | unsigned long flags; | ||
73 | int change; | ||
74 | unsigned char nval; | ||
75 | |||
76 | nval = ucontrol->value.integer.value[0] & 31; | ||
77 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
78 | change = gus->joystick_dac != nval; | ||
79 | gus->joystick_dac = nval; | ||
80 | snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); | ||
81 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
82 | return change; | ||
83 | } | ||
84 | |||
85 | static snd_kcontrol_new_t snd_gus_joystick_control = { | ||
86 | .iface = SNDRV_CTL_ELEM_IFACE_CARD, | ||
87 | .name = "Joystick Speed", | ||
88 | .info = snd_gus_joystick_info, | ||
89 | .get = snd_gus_joystick_get, | ||
90 | .put = snd_gus_joystick_put | ||
91 | }; | ||
92 | |||
93 | static void snd_gus_init_control(snd_gus_card_t *gus) | ||
94 | { | ||
95 | if (!gus->ace_flag) | ||
96 | snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus)); | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * | ||
101 | */ | ||
102 | |||
103 | static int snd_gus_free(snd_gus_card_t *gus) | ||
104 | { | ||
105 | if (gus->gf1.res_port2 == NULL) | ||
106 | goto __hw_end; | ||
107 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
108 | if (gus->seq_dev) { | ||
109 | snd_device_free(gus->card, gus->seq_dev); | ||
110 | gus->seq_dev = NULL; | ||
111 | } | ||
112 | #endif | ||
113 | snd_gf1_stop(gus); | ||
114 | snd_gus_init_dma_irq(gus, 0); | ||
115 | __hw_end: | ||
116 | if (gus->gf1.res_port1) { | ||
117 | release_resource(gus->gf1.res_port1); | ||
118 | kfree_nocheck(gus->gf1.res_port1); | ||
119 | } | ||
120 | if (gus->gf1.res_port2) { | ||
121 | release_resource(gus->gf1.res_port2); | ||
122 | kfree_nocheck(gus->gf1.res_port2); | ||
123 | } | ||
124 | if (gus->gf1.irq >= 0) | ||
125 | free_irq(gus->gf1.irq, (void *) gus); | ||
126 | if (gus->gf1.dma1 >= 0) { | ||
127 | disable_dma(gus->gf1.dma1); | ||
128 | free_dma(gus->gf1.dma1); | ||
129 | } | ||
130 | if (!gus->equal_dma && gus->gf1.dma2 >= 0) { | ||
131 | disable_dma(gus->gf1.dma2); | ||
132 | free_dma(gus->gf1.dma2); | ||
133 | } | ||
134 | kfree(gus); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int snd_gus_dev_free(snd_device_t *device) | ||
139 | { | ||
140 | snd_gus_card_t *gus = device->device_data; | ||
141 | return snd_gus_free(gus); | ||
142 | } | ||
143 | |||
144 | int snd_gus_create(snd_card_t * card, | ||
145 | unsigned long port, | ||
146 | int irq, int dma1, int dma2, | ||
147 | int timer_dev, | ||
148 | int voices, | ||
149 | int pcm_channels, | ||
150 | int effect, | ||
151 | snd_gus_card_t **rgus) | ||
152 | { | ||
153 | snd_gus_card_t *gus; | ||
154 | int err; | ||
155 | static snd_device_ops_t ops = { | ||
156 | .dev_free = snd_gus_dev_free, | ||
157 | }; | ||
158 | |||
159 | *rgus = NULL; | ||
160 | gus = kcalloc(1, sizeof(*gus), GFP_KERNEL); | ||
161 | if (gus == NULL) | ||
162 | return -ENOMEM; | ||
163 | gus->gf1.irq = -1; | ||
164 | gus->gf1.dma1 = -1; | ||
165 | gus->gf1.dma2 = -1; | ||
166 | gus->card = card; | ||
167 | gus->gf1.port = port; | ||
168 | /* fill register variables for speedup */ | ||
169 | gus->gf1.reg_page = GUSP(gus, GF1PAGE); | ||
170 | gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL); | ||
171 | gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH); | ||
172 | gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW); | ||
173 | gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT); | ||
174 | gus->gf1.reg_dram = GUSP(gus, DRAM); | ||
175 | gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL); | ||
176 | gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA); | ||
177 | /* allocate resources */ | ||
178 | if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) { | ||
179 | snd_printk(KERN_ERR "gus: can't grab SB port 0x%lx\n", port); | ||
180 | snd_gus_free(gus); | ||
181 | return -EBUSY; | ||
182 | } | ||
183 | if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) { | ||
184 | snd_printk(KERN_ERR "gus: can't grab synth port 0x%lx\n", port + 0x100); | ||
185 | snd_gus_free(gus); | ||
186 | return -EBUSY; | ||
187 | } | ||
188 | if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) { | ||
189 | snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq); | ||
190 | snd_gus_free(gus); | ||
191 | return -EBUSY; | ||
192 | } | ||
193 | gus->gf1.irq = irq; | ||
194 | if (request_dma(dma1, "GUS - 1")) { | ||
195 | snd_printk(KERN_ERR "gus: can't grab DMA1 %d\n", dma1); | ||
196 | snd_gus_free(gus); | ||
197 | return -EBUSY; | ||
198 | } | ||
199 | gus->gf1.dma1 = dma1; | ||
200 | if (dma2 >= 0 && dma1 != dma2) { | ||
201 | if (request_dma(dma2, "GUS - 2")) { | ||
202 | snd_printk(KERN_ERR "gus: can't grab DMA2 %d\n", dma2); | ||
203 | snd_gus_free(gus); | ||
204 | return -EBUSY; | ||
205 | } | ||
206 | gus->gf1.dma2 = dma2; | ||
207 | } else { | ||
208 | gus->gf1.dma2 = gus->gf1.dma1; | ||
209 | gus->equal_dma = 1; | ||
210 | } | ||
211 | gus->timer_dev = timer_dev; | ||
212 | if (voices < 14) | ||
213 | voices = 14; | ||
214 | if (voices > 32) | ||
215 | voices = 32; | ||
216 | if (pcm_channels < 0) | ||
217 | pcm_channels = 0; | ||
218 | if (pcm_channels > 8) | ||
219 | pcm_channels = 8; | ||
220 | pcm_channels++; | ||
221 | pcm_channels &= ~1; | ||
222 | gus->gf1.effect = effect ? 1 : 0; | ||
223 | gus->gf1.active_voices = voices; | ||
224 | gus->gf1.pcm_channels = pcm_channels; | ||
225 | gus->gf1.volume_ramp = 25; | ||
226 | gus->gf1.smooth_pan = 1; | ||
227 | spin_lock_init(&gus->reg_lock); | ||
228 | spin_lock_init(&gus->voice_alloc); | ||
229 | spin_lock_init(&gus->active_voice_lock); | ||
230 | spin_lock_init(&gus->event_lock); | ||
231 | spin_lock_init(&gus->dma_lock); | ||
232 | spin_lock_init(&gus->pcm_volume_level_lock); | ||
233 | spin_lock_init(&gus->uart_cmd_lock); | ||
234 | init_MUTEX(&gus->dma_mutex); | ||
235 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) { | ||
236 | snd_gus_free(gus); | ||
237 | return err; | ||
238 | } | ||
239 | *rgus = gus; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * Memory detection routine for plain GF1 soundcards | ||
245 | */ | ||
246 | |||
247 | static int snd_gus_detect_memory(snd_gus_card_t * gus) | ||
248 | { | ||
249 | int l, idx, local; | ||
250 | unsigned char d; | ||
251 | |||
252 | snd_gf1_poke(gus, 0L, 0xaa); | ||
253 | snd_gf1_poke(gus, 1L, 0x55); | ||
254 | if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) { | ||
255 | snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port); | ||
256 | return -ENOMEM; | ||
257 | } | ||
258 | for (idx = 1, d = 0xab; idx < 4; idx++, d++) { | ||
259 | local = idx << 18; | ||
260 | snd_gf1_poke(gus, local, d); | ||
261 | snd_gf1_poke(gus, local + 1, d + 1); | ||
262 | if (snd_gf1_peek(gus, local) != d || | ||
263 | snd_gf1_peek(gus, local + 1) != d + 1 || | ||
264 | snd_gf1_peek(gus, 0L) != 0xaa) | ||
265 | break; | ||
266 | } | ||
267 | #if 1 | ||
268 | gus->gf1.memory = idx << 18; | ||
269 | #else | ||
270 | gus->gf1.memory = 256 * 1024; | ||
271 | #endif | ||
272 | for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) { | ||
273 | gus->gf1.mem_alloc.banks_8[l].address = | ||
274 | gus->gf1.mem_alloc.banks_8[l].size = 0; | ||
275 | gus->gf1.mem_alloc.banks_16[l].address = l << 18; | ||
276 | gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0; | ||
277 | } | ||
278 | gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory; | ||
279 | return 0; /* some memory were detected */ | ||
280 | } | ||
281 | |||
282 | static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches) | ||
283 | { | ||
284 | snd_card_t *card; | ||
285 | unsigned long flags; | ||
286 | int irq, dma1, dma2; | ||
287 | static unsigned char irqs[16] = | ||
288 | {0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; | ||
289 | static unsigned char dmas[8] = | ||
290 | {6, 1, 0, 2, 0, 3, 4, 5}; | ||
291 | |||
292 | snd_assert(gus != NULL, return -EINVAL); | ||
293 | card = gus->card; | ||
294 | snd_assert(card != NULL, return -EINVAL); | ||
295 | |||
296 | gus->mix_cntrl_reg &= 0xf8; | ||
297 | gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */ | ||
298 | if (gus->codec_flag || gus->ess_flag) { | ||
299 | gus->mix_cntrl_reg &= ~1; /* enable LINE IN */ | ||
300 | gus->mix_cntrl_reg |= 4; /* enable MIC */ | ||
301 | } | ||
302 | dma1 = gus->gf1.dma1; | ||
303 | dma1 = dma1 < 0 ? -dma1 : dma1; | ||
304 | dma1 = dmas[dma1 & 7]; | ||
305 | dma2 = gus->gf1.dma2; | ||
306 | dma2 = dma2 < 0 ? -dma2 : dma2; | ||
307 | dma2 = dmas[dma2 & 7]; | ||
308 | #if 0 | ||
309 | printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2); | ||
310 | #endif | ||
311 | dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); | ||
312 | |||
313 | if ((dma1 & 7) == 0 || (dma2 & 7) == 0) { | ||
314 | snd_printk("Error! DMA isn't defined.\n"); | ||
315 | return -EINVAL; | ||
316 | } | ||
317 | irq = gus->gf1.irq; | ||
318 | irq = irq < 0 ? -irq : irq; | ||
319 | irq = irqs[irq & 0x0f]; | ||
320 | if (irq == 0) { | ||
321 | snd_printk("Error! IRQ isn't defined.\n"); | ||
322 | return -EINVAL; | ||
323 | } | ||
324 | irq |= 0x40; | ||
325 | #if 0 | ||
326 | card->mixer.mix_ctrl_reg |= 0x10; | ||
327 | #endif | ||
328 | |||
329 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
330 | outb(5, GUSP(gus, REGCNTRLS)); | ||
331 | outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
332 | outb(0x00, GUSP(gus, IRQDMACNTRLREG)); | ||
333 | outb(0, GUSP(gus, REGCNTRLS)); | ||
334 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
335 | |||
336 | udelay(100); | ||
337 | |||
338 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
339 | outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
340 | outb(dma1, GUSP(gus, IRQDMACNTRLREG)); | ||
341 | if (latches) { | ||
342 | outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
343 | outb(irq, GUSP(gus, IRQDMACNTRLREG)); | ||
344 | } | ||
345 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
346 | |||
347 | udelay(100); | ||
348 | |||
349 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
350 | outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
351 | outb(dma1, GUSP(gus, IRQDMACNTRLREG)); | ||
352 | if (latches) { | ||
353 | outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
354 | outb(irq, GUSP(gus, IRQDMACNTRLREG)); | ||
355 | } | ||
356 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
357 | |||
358 | snd_gf1_delay(gus); | ||
359 | |||
360 | if (latches) | ||
361 | gus->mix_cntrl_reg |= 0x08; /* enable latches */ | ||
362 | else | ||
363 | gus->mix_cntrl_reg &= ~0x08; /* disable latches */ | ||
364 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
365 | outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
366 | outb(0, GUSP(gus, GF1PAGE)); | ||
367 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int snd_gus_check_version(snd_gus_card_t * gus) | ||
373 | { | ||
374 | unsigned long flags; | ||
375 | unsigned char val, rev; | ||
376 | snd_card_t *card; | ||
377 | |||
378 | card = gus->card; | ||
379 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
380 | outb(0x20, GUSP(gus, REGCNTRLS)); | ||
381 | val = inb(GUSP(gus, REGCNTRLS)); | ||
382 | rev = inb(GUSP(gus, BOARDVERSION)); | ||
383 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
384 | snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); | ||
385 | strcpy(card->driver, "GUS"); | ||
386 | strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); | ||
387 | if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { | ||
388 | if (rev >= 5 && rev <= 9) { | ||
389 | gus->ics_flag = 1; | ||
390 | if (rev == 5) | ||
391 | gus->ics_flipped = 1; | ||
392 | card->longname[27] = '3'; | ||
393 | card->longname[29] = rev == 5 ? '5' : '7'; | ||
394 | } | ||
395 | if (rev >= 10 && rev != 255) { | ||
396 | if (rev >= 10 && rev <= 11) { | ||
397 | strcpy(card->driver, "GUS MAX"); | ||
398 | strcpy(card->longname, "Gravis UltraSound MAX"); | ||
399 | gus->max_flag = 1; | ||
400 | } else if (rev == 0x30) { | ||
401 | strcpy(card->driver, "GUS ACE"); | ||
402 | strcpy(card->longname, "Gravis UltraSound Ace"); | ||
403 | gus->ace_flag = 1; | ||
404 | } else if (rev == 0x50) { | ||
405 | strcpy(card->driver, "GUS Extreme"); | ||
406 | strcpy(card->longname, "Gravis UltraSound Extreme"); | ||
407 | gus->ess_flag = 1; | ||
408 | } else { | ||
409 | snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val); | ||
410 | snd_printk(" please - report to <perex@suse.cz>\n"); | ||
411 | } | ||
412 | } | ||
413 | } | ||
414 | strcpy(card->shortname, card->longname); | ||
415 | gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */ | ||
416 | snd_gus_init_control(gus); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) | ||
421 | { | ||
422 | snd_gus_card_t *gus = seq_dev->private_data; | ||
423 | gus->seq_dev = NULL; | ||
424 | } | ||
425 | |||
426 | int snd_gus_initialize(snd_gus_card_t *gus) | ||
427 | { | ||
428 | int err; | ||
429 | |||
430 | if (!gus->interwave) { | ||
431 | if ((err = snd_gus_check_version(gus)) < 0) { | ||
432 | snd_printk("version check failed\n"); | ||
433 | return err; | ||
434 | } | ||
435 | if ((err = snd_gus_detect_memory(gus)) < 0) | ||
436 | return err; | ||
437 | } | ||
438 | if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) | ||
439 | return err; | ||
440 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
441 | if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS, | ||
442 | sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) { | ||
443 | strcpy(gus->seq_dev->name, "GUS"); | ||
444 | *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus; | ||
445 | gus->seq_dev->private_data = gus; | ||
446 | gus->seq_dev->private_free = snd_gus_seq_dev_free; | ||
447 | } | ||
448 | #endif | ||
449 | snd_gf1_start(gus); | ||
450 | gus->initialized = 1; | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | /* gus_io.c */ | ||
455 | EXPORT_SYMBOL(snd_gf1_delay); | ||
456 | EXPORT_SYMBOL(snd_gf1_write8); | ||
457 | EXPORT_SYMBOL(snd_gf1_look8); | ||
458 | EXPORT_SYMBOL(snd_gf1_write16); | ||
459 | EXPORT_SYMBOL(snd_gf1_look16); | ||
460 | EXPORT_SYMBOL(snd_gf1_i_write8); | ||
461 | EXPORT_SYMBOL(snd_gf1_i_look8); | ||
462 | EXPORT_SYMBOL(snd_gf1_i_write16); | ||
463 | EXPORT_SYMBOL(snd_gf1_i_look16); | ||
464 | EXPORT_SYMBOL(snd_gf1_dram_addr); | ||
465 | EXPORT_SYMBOL(snd_gf1_write_addr); | ||
466 | EXPORT_SYMBOL(snd_gf1_poke); | ||
467 | EXPORT_SYMBOL(snd_gf1_peek); | ||
468 | /* gus_reset.c */ | ||
469 | EXPORT_SYMBOL(snd_gf1_alloc_voice); | ||
470 | EXPORT_SYMBOL(snd_gf1_free_voice); | ||
471 | EXPORT_SYMBOL(snd_gf1_ctrl_stop); | ||
472 | EXPORT_SYMBOL(snd_gf1_stop_voice); | ||
473 | EXPORT_SYMBOL(snd_gf1_start); | ||
474 | EXPORT_SYMBOL(snd_gf1_stop); | ||
475 | /* gus_mixer.c */ | ||
476 | EXPORT_SYMBOL(snd_gf1_new_mixer); | ||
477 | /* gus_pcm.c */ | ||
478 | EXPORT_SYMBOL(snd_gf1_pcm_new); | ||
479 | /* gus.c */ | ||
480 | EXPORT_SYMBOL(snd_gus_use_inc); | ||
481 | EXPORT_SYMBOL(snd_gus_use_dec); | ||
482 | EXPORT_SYMBOL(snd_gus_create); | ||
483 | EXPORT_SYMBOL(snd_gus_initialize); | ||
484 | /* gus_irq.c */ | ||
485 | EXPORT_SYMBOL(snd_gus_interrupt); | ||
486 | /* gus_uart.c */ | ||
487 | EXPORT_SYMBOL(snd_gf1_rawmidi_new); | ||
488 | /* gus_dram.c */ | ||
489 | EXPORT_SYMBOL(snd_gus_dram_write); | ||
490 | EXPORT_SYMBOL(snd_gus_dram_read); | ||
491 | /* gus_volume.c */ | ||
492 | EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw); | ||
493 | EXPORT_SYMBOL(snd_gf1_translate_freq); | ||
494 | /* gus_mem.c */ | ||
495 | EXPORT_SYMBOL(snd_gf1_mem_alloc); | ||
496 | EXPORT_SYMBOL(snd_gf1_mem_xfree); | ||
497 | EXPORT_SYMBOL(snd_gf1_mem_free); | ||
498 | EXPORT_SYMBOL(snd_gf1_mem_lock); | ||
499 | |||
500 | /* | ||
501 | * INIT part | ||
502 | */ | ||
503 | |||
504 | static int __init alsa_gus_init(void) | ||
505 | { | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | static void __exit alsa_gus_exit(void) | ||
510 | { | ||
511 | } | ||
512 | |||
513 | module_init(alsa_gus_init) | ||
514 | module_exit(alsa_gus_exit) | ||
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c new file mode 100644 index 000000000000..bfc2b91001d5 --- /dev/null +++ b/sound/isa/gus/gus_mem.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * GUS's memory allocation routines / bottom layer | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include <sound/info.h> | ||
27 | |||
28 | #ifdef CONFIG_SND_DEBUG | ||
29 | static void snd_gf1_mem_info_read(snd_info_entry_t *entry, | ||
30 | snd_info_buffer_t * buffer); | ||
31 | #endif | ||
32 | |||
33 | void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup) | ||
34 | { | ||
35 | if (!xup) { | ||
36 | down(&alloc->memory_mutex); | ||
37 | } else { | ||
38 | up(&alloc->memory_mutex); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc, | ||
43 | snd_gf1_mem_block_t * block) | ||
44 | { | ||
45 | snd_gf1_mem_block_t *pblock, *nblock; | ||
46 | |||
47 | nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL); | ||
48 | if (nblock == NULL) | ||
49 | return NULL; | ||
50 | *nblock = *block; | ||
51 | pblock = alloc->first; | ||
52 | while (pblock) { | ||
53 | if (pblock->ptr > nblock->ptr) { | ||
54 | nblock->prev = pblock->prev; | ||
55 | nblock->next = pblock; | ||
56 | pblock->prev = nblock; | ||
57 | if (pblock == alloc->first) | ||
58 | alloc->first = nblock; | ||
59 | else | ||
60 | nblock->prev->next = nblock; | ||
61 | up(&alloc->memory_mutex); | ||
62 | return NULL; | ||
63 | } | ||
64 | pblock = pblock->next; | ||
65 | } | ||
66 | nblock->next = NULL; | ||
67 | if (alloc->last == NULL) { | ||
68 | nblock->prev = NULL; | ||
69 | alloc->first = alloc->last = nblock; | ||
70 | } else { | ||
71 | nblock->prev = alloc->last; | ||
72 | alloc->last->next = nblock; | ||
73 | alloc->last = nblock; | ||
74 | } | ||
75 | return nblock; | ||
76 | } | ||
77 | |||
78 | int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block) | ||
79 | { | ||
80 | if (block->share) { /* ok.. shared block */ | ||
81 | block->share--; | ||
82 | up(&alloc->memory_mutex); | ||
83 | return 0; | ||
84 | } | ||
85 | if (alloc->first == block) { | ||
86 | alloc->first = block->next; | ||
87 | if (block->next) | ||
88 | block->next->prev = NULL; | ||
89 | } else { | ||
90 | block->prev->next = block->next; | ||
91 | if (block->next) | ||
92 | block->next->prev = block->prev; | ||
93 | } | ||
94 | if (alloc->last == block) { | ||
95 | alloc->last = block->prev; | ||
96 | if (block->prev) | ||
97 | block->prev->next = NULL; | ||
98 | } else { | ||
99 | block->next->prev = block->prev; | ||
100 | if (block->prev) | ||
101 | block->prev->next = block->next; | ||
102 | } | ||
103 | kfree(block->name); | ||
104 | kfree(block); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, | ||
109 | unsigned int address) | ||
110 | { | ||
111 | snd_gf1_mem_block_t *block; | ||
112 | |||
113 | for (block = alloc->first; block; block = block->next) { | ||
114 | if (block->ptr == address) { | ||
115 | return block; | ||
116 | } | ||
117 | } | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, | ||
122 | unsigned int *share_id) | ||
123 | { | ||
124 | snd_gf1_mem_block_t *block; | ||
125 | |||
126 | if (!share_id[0] && !share_id[1] && | ||
127 | !share_id[2] && !share_id[3]) | ||
128 | return NULL; | ||
129 | for (block = alloc->first; block; block = block->next) | ||
130 | if (!memcmp(share_id, block->share_id, sizeof(share_id))) | ||
131 | return block; | ||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | static int snd_gf1_mem_find(snd_gf1_mem_t * alloc, | ||
136 | snd_gf1_mem_block_t * block, | ||
137 | unsigned int size, int w_16, int align) | ||
138 | { | ||
139 | snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8; | ||
140 | unsigned int idx, boundary; | ||
141 | int size1; | ||
142 | snd_gf1_mem_block_t *pblock; | ||
143 | unsigned int ptr1, ptr2; | ||
144 | |||
145 | align--; | ||
146 | if (w_16 && align < 1) | ||
147 | align = 1; | ||
148 | block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0; | ||
149 | block->owner = SNDRV_GF1_MEM_OWNER_DRIVER; | ||
150 | block->share = 0; | ||
151 | block->share_id[0] = block->share_id[1] = | ||
152 | block->share_id[2] = block->share_id[3] = 0; | ||
153 | block->name = NULL; | ||
154 | block->prev = block->next = NULL; | ||
155 | for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) { | ||
156 | while (pblock->ptr >= (boundary = info[idx].address + info[idx].size)) | ||
157 | idx++; | ||
158 | while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size)) | ||
159 | idx++; | ||
160 | ptr2 = boundary; | ||
161 | if (pblock->next) { | ||
162 | if (pblock->ptr + pblock->size == pblock->next->ptr) | ||
163 | continue; | ||
164 | if (pblock->next->ptr < boundary) | ||
165 | ptr2 = pblock->next->ptr; | ||
166 | } | ||
167 | ptr1 = (pblock->ptr + pblock->size + align) & ~align; | ||
168 | if (ptr1 >= ptr2) | ||
169 | continue; | ||
170 | size1 = ptr2 - ptr1; | ||
171 | if ((int)size <= size1) { | ||
172 | block->ptr = ptr1; | ||
173 | block->size = size; | ||
174 | return 0; | ||
175 | } | ||
176 | } | ||
177 | while (++idx < 4) { | ||
178 | if (size <= info[idx].size) { | ||
179 | /* I assume that bank address is already aligned.. */ | ||
180 | block->ptr = info[idx].address; | ||
181 | block->size = size; | ||
182 | return 0; | ||
183 | } | ||
184 | } | ||
185 | return -ENOMEM; | ||
186 | } | ||
187 | |||
188 | snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, | ||
189 | char *name, int size, int w_16, int align, | ||
190 | unsigned int *share_id) | ||
191 | { | ||
192 | snd_gf1_mem_block_t block, *nblock; | ||
193 | |||
194 | snd_gf1_mem_lock(alloc, 0); | ||
195 | if (share_id != NULL) { | ||
196 | nblock = snd_gf1_mem_share(alloc, share_id); | ||
197 | if (nblock != NULL) { | ||
198 | if (size != (int)nblock->size) { | ||
199 | /* TODO: remove in the future */ | ||
200 | snd_printk("snd_gf1_mem_alloc - share: sizes differ\n"); | ||
201 | goto __std; | ||
202 | } | ||
203 | nblock->share++; | ||
204 | snd_gf1_mem_lock(alloc, 1); | ||
205 | return NULL; | ||
206 | } | ||
207 | } | ||
208 | __std: | ||
209 | if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) { | ||
210 | snd_gf1_mem_lock(alloc, 1); | ||
211 | return NULL; | ||
212 | } | ||
213 | if (share_id != NULL) | ||
214 | memcpy(&block.share_id, share_id, sizeof(block.share_id)); | ||
215 | block.owner = owner; | ||
216 | block.name = snd_kmalloc_strdup(name, GFP_KERNEL); | ||
217 | nblock = snd_gf1_mem_xalloc(alloc, &block); | ||
218 | snd_gf1_mem_lock(alloc, 1); | ||
219 | return nblock; | ||
220 | } | ||
221 | |||
222 | int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address) | ||
223 | { | ||
224 | int result; | ||
225 | snd_gf1_mem_block_t *block; | ||
226 | |||
227 | snd_gf1_mem_lock(alloc, 0); | ||
228 | if ((block = snd_gf1_mem_look(alloc, address)) != NULL) { | ||
229 | result = snd_gf1_mem_xfree(alloc, block); | ||
230 | snd_gf1_mem_lock(alloc, 1); | ||
231 | return result; | ||
232 | } | ||
233 | snd_gf1_mem_lock(alloc, 1); | ||
234 | return -EINVAL; | ||
235 | } | ||
236 | |||
237 | int snd_gf1_mem_init(snd_gus_card_t * gus) | ||
238 | { | ||
239 | snd_gf1_mem_t *alloc; | ||
240 | snd_gf1_mem_block_t block; | ||
241 | #ifdef CONFIG_SND_DEBUG | ||
242 | snd_info_entry_t *entry; | ||
243 | #endif | ||
244 | |||
245 | alloc = &gus->gf1.mem_alloc; | ||
246 | init_MUTEX(&alloc->memory_mutex); | ||
247 | alloc->first = alloc->last = NULL; | ||
248 | if (!gus->gf1.memory) | ||
249 | return 0; | ||
250 | |||
251 | memset(&block, 0, sizeof(block)); | ||
252 | block.owner = SNDRV_GF1_MEM_OWNER_DRIVER; | ||
253 | if (gus->gf1.enh_mode) { | ||
254 | block.ptr = 0; | ||
255 | block.size = 1024; | ||
256 | block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL); | ||
257 | if (snd_gf1_mem_xalloc(alloc, &block) == NULL) | ||
258 | return -ENOMEM; | ||
259 | } | ||
260 | block.ptr = gus->gf1.default_voice_address; | ||
261 | block.size = 4; | ||
262 | block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL); | ||
263 | if (snd_gf1_mem_xalloc(alloc, &block) == NULL) | ||
264 | return -ENOMEM; | ||
265 | #ifdef CONFIG_SND_DEBUG | ||
266 | if (! snd_card_proc_new(gus->card, "gusmem", &entry)) { | ||
267 | snd_info_set_text_ops(entry, gus, 1024, snd_gf1_mem_info_read); | ||
268 | entry->c.text.read_size = 256 * 1024; | ||
269 | } | ||
270 | #endif | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | int snd_gf1_mem_done(snd_gus_card_t * gus) | ||
275 | { | ||
276 | snd_gf1_mem_t *alloc; | ||
277 | snd_gf1_mem_block_t *block, *nblock; | ||
278 | |||
279 | alloc = &gus->gf1.mem_alloc; | ||
280 | block = alloc->first; | ||
281 | while (block) { | ||
282 | nblock = block->next; | ||
283 | snd_gf1_mem_xfree(alloc, block); | ||
284 | block = nblock; | ||
285 | } | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | #ifdef CONFIG_SND_DEBUG | ||
290 | static void snd_gf1_mem_info_read(snd_info_entry_t *entry, | ||
291 | snd_info_buffer_t * buffer) | ||
292 | { | ||
293 | snd_gus_card_t *gus; | ||
294 | snd_gf1_mem_t *alloc; | ||
295 | snd_gf1_mem_block_t *block; | ||
296 | unsigned int total, used; | ||
297 | int i; | ||
298 | |||
299 | gus = entry->private_data; | ||
300 | alloc = &gus->gf1.mem_alloc; | ||
301 | down(&alloc->memory_mutex); | ||
302 | snd_iprintf(buffer, "8-bit banks : \n "); | ||
303 | for (i = 0; i < 4; i++) | ||
304 | snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : ""); | ||
305 | snd_iprintf(buffer, "\n" | ||
306 | "16-bit banks : \n "); | ||
307 | for (i = total = 0; i < 4; i++) { | ||
308 | snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : ""); | ||
309 | total += alloc->banks_16[i].size; | ||
310 | } | ||
311 | snd_iprintf(buffer, "\n"); | ||
312 | used = 0; | ||
313 | for (block = alloc->first, i = 0; block; block = block->next, i++) { | ||
314 | used += block->size; | ||
315 | snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size); | ||
316 | if (block->share || | ||
317 | block->share_id[0] || block->share_id[1] || | ||
318 | block->share_id[2] || block->share_id[3]) | ||
319 | snd_iprintf(buffer, " Share : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n", | ||
320 | block->share, | ||
321 | block->share_id[0], block->share_id[1], | ||
322 | block->share_id[2], block->share_id[3]); | ||
323 | snd_iprintf(buffer, " Flags :%s\n", | ||
324 | block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : ""); | ||
325 | snd_iprintf(buffer, " Owner : "); | ||
326 | switch (block->owner) { | ||
327 | case SNDRV_GF1_MEM_OWNER_DRIVER: | ||
328 | snd_iprintf(buffer, "driver - %s\n", block->name); | ||
329 | break; | ||
330 | case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE: | ||
331 | snd_iprintf(buffer, "SIMPLE wave\n"); | ||
332 | break; | ||
333 | case SNDRV_GF1_MEM_OWNER_WAVE_GF1: | ||
334 | snd_iprintf(buffer, "GF1 wave\n"); | ||
335 | break; | ||
336 | case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF: | ||
337 | snd_iprintf(buffer, "IWFFFF wave\n"); | ||
338 | break; | ||
339 | default: | ||
340 | snd_iprintf(buffer, "unknown\n"); | ||
341 | } | ||
342 | } | ||
343 | snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n", | ||
344 | total, used, total - used); | ||
345 | up(&alloc->memory_mutex); | ||
346 | #if 0 | ||
347 | ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n", | ||
348 | ultra_memory_free_size(card, &card->gf1.mem_alloc), | ||
349 | ultra_memory_free_block(card, &card->gf1.mem_alloc, 0), | ||
350 | ultra_memory_free_block(card, &card->gf1.mem_alloc, 1)); | ||
351 | #endif | ||
352 | } | ||
353 | #endif | ||
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c new file mode 100644 index 000000000000..886763f12132 --- /dev/null +++ b/sound/isa/gus/gus_mem_proc.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * GUS's memory access via proc filesystem | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include <sound/info.h> | ||
27 | |||
28 | typedef struct gus_proc_private { | ||
29 | int rom; /* data are in ROM */ | ||
30 | unsigned int address; | ||
31 | unsigned int size; | ||
32 | snd_gus_card_t * gus; | ||
33 | } gus_proc_private_t; | ||
34 | |||
35 | static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data, | ||
36 | struct file *file, char __user *buf, | ||
37 | unsigned long count, unsigned long pos) | ||
38 | { | ||
39 | long size; | ||
40 | gus_proc_private_t *priv = entry->private_data; | ||
41 | snd_gus_card_t *gus = priv->gus; | ||
42 | int err; | ||
43 | |||
44 | size = count; | ||
45 | if (pos + size > priv->size) | ||
46 | size = (long)priv->size - pos; | ||
47 | if (size > 0) { | ||
48 | if ((err = snd_gus_dram_read(gus, buf, pos, size, priv->rom)) < 0) | ||
49 | return err; | ||
50 | return size; | ||
51 | } | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry, | ||
56 | void *private_file_data, | ||
57 | struct file *file, | ||
58 | long long offset, | ||
59 | int orig) | ||
60 | { | ||
61 | gus_proc_private_t *priv = entry->private_data; | ||
62 | |||
63 | switch (orig) { | ||
64 | case 0: /* SEEK_SET */ | ||
65 | file->f_pos = offset; | ||
66 | break; | ||
67 | case 1: /* SEEK_CUR */ | ||
68 | file->f_pos += offset; | ||
69 | break; | ||
70 | case 2: /* SEEK_END, offset is negative */ | ||
71 | file->f_pos = priv->size + offset; | ||
72 | break; | ||
73 | default: | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | if (file->f_pos > priv->size) | ||
77 | file->f_pos = priv->size; | ||
78 | return file->f_pos; | ||
79 | } | ||
80 | |||
81 | static void snd_gf1_mem_proc_free(snd_info_entry_t *entry) | ||
82 | { | ||
83 | gus_proc_private_t *priv = entry->private_data; | ||
84 | kfree(priv); | ||
85 | } | ||
86 | |||
87 | static struct snd_info_entry_ops snd_gf1_mem_proc_ops = { | ||
88 | .read = snd_gf1_mem_proc_dump, | ||
89 | .llseek = snd_gf1_mem_proc_llseek, | ||
90 | }; | ||
91 | |||
92 | int snd_gf1_mem_proc_init(snd_gus_card_t * gus) | ||
93 | { | ||
94 | int idx; | ||
95 | char name[16]; | ||
96 | gus_proc_private_t *priv; | ||
97 | snd_info_entry_t *entry; | ||
98 | |||
99 | for (idx = 0; idx < 4; idx++) { | ||
100 | if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { | ||
101 | priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); | ||
102 | if (priv == NULL) | ||
103 | return -ENOMEM; | ||
104 | priv->gus = gus; | ||
105 | sprintf(name, "gus-ram-%i", idx); | ||
106 | if (! snd_card_proc_new(gus->card, name, &entry)) { | ||
107 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
108 | entry->private_data = priv; | ||
109 | entry->private_free = snd_gf1_mem_proc_free; | ||
110 | entry->c.ops = &snd_gf1_mem_proc_ops; | ||
111 | priv->address = gus->gf1.mem_alloc.banks_8[idx].address; | ||
112 | priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | for (idx = 0; idx < 4; idx++) { | ||
117 | if (gus->gf1.rom_present & (1 << idx)) { | ||
118 | priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); | ||
119 | if (priv == NULL) | ||
120 | return -ENOMEM; | ||
121 | priv->rom = 1; | ||
122 | priv->gus = gus; | ||
123 | sprintf(name, "gus-rom-%i", idx); | ||
124 | if (! snd_card_proc_new(gus->card, name, &entry)) { | ||
125 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
126 | entry->private_data = priv; | ||
127 | entry->private_free = snd_gf1_mem_proc_free; | ||
128 | entry->c.ops = &snd_gf1_mem_proc_ops; | ||
129 | priv->address = idx * 4096 * 1024; | ||
130 | priv->size = entry->size = gus->gf1.rom_memory; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | return 0; | ||
135 | } | ||
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c new file mode 100644 index 000000000000..a051094d510e --- /dev/null +++ b/sound/isa/gus/gus_mixer.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of ICS 2101 chip and "mixer" in GF1 chip | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/control.h> | ||
27 | #include <sound/gus.h> | ||
28 | |||
29 | /* | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #define GF1_SINGLE(xname, xindex, shift, invert) \ | ||
34 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
35 | .info = snd_gf1_info_single, \ | ||
36 | .get = snd_gf1_get_single, .put = snd_gf1_put_single, \ | ||
37 | .private_value = shift | (invert << 8) } | ||
38 | |||
39 | static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
40 | { | ||
41 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
42 | uinfo->count = 1; | ||
43 | uinfo->value.integer.min = 0; | ||
44 | uinfo->value.integer.max = 1; | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int snd_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
49 | { | ||
50 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
51 | int shift = kcontrol->private_value & 0xff; | ||
52 | int invert = (kcontrol->private_value >> 8) & 1; | ||
53 | |||
54 | ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; | ||
55 | if (invert) | ||
56 | ucontrol->value.integer.value[0] ^= 1; | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
61 | { | ||
62 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
63 | unsigned long flags; | ||
64 | int shift = kcontrol->private_value & 0xff; | ||
65 | int invert = (kcontrol->private_value >> 8) & 1; | ||
66 | int change; | ||
67 | unsigned char oval, nval; | ||
68 | |||
69 | nval = ucontrol->value.integer.value[0] & 1; | ||
70 | if (invert) | ||
71 | nval ^= 1; | ||
72 | nval <<= shift; | ||
73 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
74 | oval = gus->mix_cntrl_reg; | ||
75 | nval = (oval & ~(1 << shift)) | nval; | ||
76 | change = nval != oval; | ||
77 | outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); | ||
78 | outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); | ||
79 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
80 | return change; | ||
81 | } | ||
82 | |||
83 | #define ICS_DOUBLE(xname, xindex, addr) \ | ||
84 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
85 | .info = snd_ics_info_double, \ | ||
86 | .get = snd_ics_get_double, .put = snd_ics_put_double, \ | ||
87 | .private_value = addr } | ||
88 | |||
89 | static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
90 | { | ||
91 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
92 | uinfo->count = 2; | ||
93 | uinfo->value.integer.min = 0; | ||
94 | uinfo->value.integer.max = 127; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
99 | { | ||
100 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
101 | unsigned long flags; | ||
102 | int addr = kcontrol->private_value & 0xff; | ||
103 | unsigned char left, right; | ||
104 | |||
105 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
106 | left = gus->gf1.ics_regs[addr][0]; | ||
107 | right = gus->gf1.ics_regs[addr][1]; | ||
108 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
109 | ucontrol->value.integer.value[0] = left & 127; | ||
110 | ucontrol->value.integer.value[1] = right & 127; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
115 | { | ||
116 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
117 | unsigned long flags; | ||
118 | int addr = kcontrol->private_value & 0xff; | ||
119 | int change; | ||
120 | unsigned char val1, val2, oval1, oval2, tmp; | ||
121 | |||
122 | val1 = ucontrol->value.integer.value[0] & 127; | ||
123 | val2 = ucontrol->value.integer.value[1] & 127; | ||
124 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
125 | oval1 = gus->gf1.ics_regs[addr][0]; | ||
126 | oval2 = gus->gf1.ics_regs[addr][1]; | ||
127 | change = val1 != oval1 || val2 != oval2; | ||
128 | gus->gf1.ics_regs[addr][0] = val1; | ||
129 | gus->gf1.ics_regs[addr][1] = val2; | ||
130 | if (gus->ics_flag && gus->ics_flipped && | ||
131 | (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) { | ||
132 | tmp = val1; | ||
133 | val1 = val2; | ||
134 | val2 = tmp; | ||
135 | } | ||
136 | addr <<= 3; | ||
137 | outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); | ||
138 | outb(1, GUSP(gus, MIXDATAPORT)); | ||
139 | outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); | ||
140 | outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); | ||
141 | outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); | ||
142 | outb(2, GUSP(gus, MIXDATAPORT)); | ||
143 | outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); | ||
144 | outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); | ||
145 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
146 | return change; | ||
147 | } | ||
148 | |||
149 | static snd_kcontrol_new_t snd_gf1_controls[] = { | ||
150 | GF1_SINGLE("Master Playback Switch", 0, 1, 1), | ||
151 | GF1_SINGLE("Line Switch", 0, 0, 1), | ||
152 | GF1_SINGLE("Mic Switch", 0, 2, 0) | ||
153 | }; | ||
154 | |||
155 | static snd_kcontrol_new_t snd_ics_controls[] = { | ||
156 | GF1_SINGLE("Master Playback Switch", 0, 1, 1), | ||
157 | ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), | ||
158 | ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), | ||
159 | GF1_SINGLE("Line Switch", 0, 0, 1), | ||
160 | ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), | ||
161 | GF1_SINGLE("Mic Switch", 0, 2, 0), | ||
162 | ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), | ||
163 | ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) | ||
164 | }; | ||
165 | |||
166 | int snd_gf1_new_mixer(snd_gus_card_t * gus) | ||
167 | { | ||
168 | snd_card_t *card; | ||
169 | unsigned int idx, max; | ||
170 | int err; | ||
171 | |||
172 | snd_assert(gus != NULL, return -EINVAL); | ||
173 | card = gus->card; | ||
174 | snd_assert(card != NULL, return -EINVAL); | ||
175 | |||
176 | if (gus->ics_flag) | ||
177 | snd_component_add(card, "ICS2101"); | ||
178 | if (card->mixername[0] == '\0') { | ||
179 | strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); | ||
180 | } else { | ||
181 | if (gus->ics_flag) | ||
182 | strcat(card->mixername, ",ICS2101"); | ||
183 | strcat(card->mixername, ",GF1"); | ||
184 | } | ||
185 | |||
186 | if (!gus->ics_flag) { | ||
187 | max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls); | ||
188 | for (idx = 0; idx < max; idx++) { | ||
189 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0) | ||
190 | return err; | ||
191 | } | ||
192 | } else { | ||
193 | for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) { | ||
194 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0) | ||
195 | return err; | ||
196 | } | ||
197 | } | ||
198 | return 0; | ||
199 | } | ||
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c new file mode 100644 index 000000000000..8995ad9c516d --- /dev/null +++ b/sound/isa/gus/gus_pcm.c | |||
@@ -0,0 +1,903 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of GF1 chip (PCM things) | ||
4 | * | ||
5 | * InterWave chips supports interleaved DMA, but this feature isn't used in | ||
6 | * this code. | ||
7 | * | ||
8 | * This code emulates autoinit DMA transfer for playback, recording by GF1 | ||
9 | * chip doesn't support autoinit DMA. | ||
10 | * | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #include <sound/driver.h> | ||
29 | #include <asm/dma.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/gus.h> | ||
34 | #include <sound/pcm_params.h> | ||
35 | #include "gus_tables.h" | ||
36 | |||
37 | /* maximum rate */ | ||
38 | |||
39 | #define SNDRV_GF1_PCM_RATE 48000 | ||
40 | |||
41 | #define SNDRV_GF1_PCM_PFLG_NONE 0 | ||
42 | #define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) | ||
43 | #define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) | ||
44 | |||
45 | typedef struct { | ||
46 | snd_gus_card_t * gus; | ||
47 | snd_pcm_substream_t * substream; | ||
48 | spinlock_t lock; | ||
49 | unsigned int voices; | ||
50 | snd_gus_voice_t *pvoices[2]; | ||
51 | unsigned int memory; | ||
52 | unsigned short flags; | ||
53 | unsigned char voice_ctrl, ramp_ctrl; | ||
54 | unsigned int bpos; | ||
55 | unsigned int blocks; | ||
56 | unsigned int block_size; | ||
57 | unsigned int dma_size; | ||
58 | wait_queue_head_t sleep; | ||
59 | atomic_t dma_count; | ||
60 | int final_volume; | ||
61 | } gus_pcm_private_t; | ||
62 | |||
63 | static int snd_gf1_pcm_use_dma = 1; | ||
64 | |||
65 | static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data) | ||
66 | { | ||
67 | gus_pcm_private_t *pcmp = private_data; | ||
68 | |||
69 | if (pcmp) { | ||
70 | atomic_dec(&pcmp->dma_count); | ||
71 | wake_up(&pcmp->sleep); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream, | ||
76 | unsigned int offset, | ||
77 | unsigned int addr, | ||
78 | unsigned int count) | ||
79 | { | ||
80 | snd_gf1_dma_block_t block; | ||
81 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
82 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
83 | |||
84 | count += offset & 31; | ||
85 | offset &= ~31; | ||
86 | // snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count); | ||
87 | memset(&block, 0, sizeof(block)); | ||
88 | block.cmd = SNDRV_GF1_DMA_IRQ; | ||
89 | if (snd_pcm_format_unsigned(runtime->format)) | ||
90 | block.cmd |= SNDRV_GF1_DMA_UNSIGNED; | ||
91 | if (snd_pcm_format_width(runtime->format) == 16) | ||
92 | block.cmd |= SNDRV_GF1_DMA_16BIT; | ||
93 | block.addr = addr & ~31; | ||
94 | block.buffer = runtime->dma_area + offset; | ||
95 | block.buf_addr = runtime->dma_addr + offset; | ||
96 | block.count = count; | ||
97 | block.private_data = pcmp; | ||
98 | block.ack = snd_gf1_pcm_block_change_ack; | ||
99 | if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) | ||
100 | atomic_inc(&pcmp->dma_count); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream) | ||
105 | { | ||
106 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
107 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
108 | snd_gus_card_t * gus = pcmp->gus; | ||
109 | unsigned long flags; | ||
110 | unsigned char voice_ctrl, ramp_ctrl; | ||
111 | unsigned short rate; | ||
112 | unsigned int curr, begin, end; | ||
113 | unsigned short vol; | ||
114 | unsigned char pan; | ||
115 | unsigned int voice; | ||
116 | |||
117 | if (substream == NULL) | ||
118 | return; | ||
119 | spin_lock_irqsave(&pcmp->lock, flags); | ||
120 | if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { | ||
121 | spin_unlock_irqrestore(&pcmp->lock, flags); | ||
122 | return; | ||
123 | } | ||
124 | pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; | ||
125 | pcmp->final_volume = 0; | ||
126 | spin_unlock_irqrestore(&pcmp->lock, flags); | ||
127 | rate = snd_gf1_translate_freq(gus, runtime->rate << 4); | ||
128 | /* enable WAVE IRQ */ | ||
129 | voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; | ||
130 | /* enable RAMP IRQ + rollover */ | ||
131 | ramp_ctrl = 0x24; | ||
132 | if (pcmp->blocks == 1) { | ||
133 | voice_ctrl |= 0x08; /* loop enable */ | ||
134 | ramp_ctrl &= ~0x04; /* disable rollover */ | ||
135 | } | ||
136 | for (voice = 0; voice < pcmp->voices; voice++) { | ||
137 | begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); | ||
138 | curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; | ||
139 | end = curr + (pcmp->block_size / runtime->channels); | ||
140 | end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; | ||
141 | // snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate); | ||
142 | pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; | ||
143 | vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; | ||
144 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
145 | snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); | ||
146 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); | ||
147 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); | ||
148 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); | ||
149 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); | ||
150 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); | ||
151 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); | ||
152 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); | ||
153 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); | ||
154 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); | ||
155 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
156 | if (!gus->gf1.enh_mode) { | ||
157 | snd_gf1_delay(gus); | ||
158 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
159 | } | ||
160 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
161 | } | ||
162 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
163 | for (voice = 0; voice < pcmp->voices; voice++) { | ||
164 | snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); | ||
165 | if (gus->gf1.enh_mode) | ||
166 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ | ||
167 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
168 | voice_ctrl &= ~0x20; | ||
169 | } | ||
170 | voice_ctrl |= 0x20; | ||
171 | if (!gus->gf1.enh_mode) { | ||
172 | snd_gf1_delay(gus); | ||
173 | for (voice = 0; voice < pcmp->voices; voice++) { | ||
174 | snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); | ||
175 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
176 | voice_ctrl &= ~0x20; /* disable IRQ for next voice */ | ||
177 | } | ||
178 | } | ||
179 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
180 | } | ||
181 | |||
182 | static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice) | ||
183 | { | ||
184 | gus_pcm_private_t * pcmp; | ||
185 | snd_pcm_runtime_t * runtime; | ||
186 | unsigned char voice_ctrl, ramp_ctrl; | ||
187 | unsigned int idx; | ||
188 | unsigned int end, step; | ||
189 | |||
190 | if (!pvoice->private_data) { | ||
191 | snd_printd("snd_gf1_pcm: unknown wave irq?\n"); | ||
192 | snd_gf1_smart_stop_voice(gus, pvoice->number); | ||
193 | return; | ||
194 | } | ||
195 | pcmp = pvoice->private_data; | ||
196 | if (pcmp == NULL) { | ||
197 | snd_printd("snd_gf1_pcm: unknown wave irq?\n"); | ||
198 | snd_gf1_smart_stop_voice(gus, pvoice->number); | ||
199 | return; | ||
200 | } | ||
201 | gus = pcmp->gus; | ||
202 | runtime = pcmp->substream->runtime; | ||
203 | |||
204 | spin_lock(&gus->reg_lock); | ||
205 | snd_gf1_select_voice(gus, pvoice->number); | ||
206 | voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; | ||
207 | ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; | ||
208 | #if 0 | ||
209 | snd_gf1_select_voice(gus, pvoice->number); | ||
210 | printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); | ||
211 | snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); | ||
212 | printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); | ||
213 | snd_gf1_select_voice(gus, pvoice->number); | ||
214 | #endif | ||
215 | pcmp->bpos++; | ||
216 | pcmp->bpos %= pcmp->blocks; | ||
217 | if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ | ||
218 | voice_ctrl |= 0x08; /* enable loop */ | ||
219 | } else { | ||
220 | ramp_ctrl |= 0x04; /* enable rollover */ | ||
221 | } | ||
222 | end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); | ||
223 | end -= voice_ctrl & 4 ? 2 : 1; | ||
224 | step = pcmp->dma_size / runtime->channels; | ||
225 | voice_ctrl |= 0x20; | ||
226 | if (!pcmp->final_volume) { | ||
227 | ramp_ctrl |= 0x20; | ||
228 | ramp_ctrl &= ~0x03; | ||
229 | } | ||
230 | for (idx = 0; idx < pcmp->voices; idx++, end += step) { | ||
231 | snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); | ||
232 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); | ||
233 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
234 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
235 | voice_ctrl &= ~0x20; | ||
236 | } | ||
237 | if (!gus->gf1.enh_mode) { | ||
238 | snd_gf1_delay(gus); | ||
239 | voice_ctrl |= 0x20; | ||
240 | for (idx = 0; idx < pcmp->voices; idx++) { | ||
241 | snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); | ||
242 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
243 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
244 | voice_ctrl &= ~0x20; | ||
245 | } | ||
246 | } | ||
247 | spin_unlock(&gus->reg_lock); | ||
248 | |||
249 | snd_pcm_period_elapsed(pcmp->substream); | ||
250 | #if 0 | ||
251 | if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && | ||
252 | *runtime->state == SNDRV_PCM_STATE_RUNNING) { | ||
253 | end = pcmp->bpos * pcmp->block_size; | ||
254 | if (runtime->channels > 1) { | ||
255 | snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); | ||
256 | snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); | ||
257 | } else { | ||
258 | snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); | ||
259 | } | ||
260 | } | ||
261 | #endif | ||
262 | } | ||
263 | |||
264 | static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice) | ||
265 | { | ||
266 | unsigned short vol; | ||
267 | int cvoice; | ||
268 | gus_pcm_private_t *pcmp = pvoice->private_data; | ||
269 | |||
270 | /* stop ramp, but leave rollover bit untouched */ | ||
271 | spin_lock(&gus->reg_lock); | ||
272 | snd_gf1_select_voice(gus, pvoice->number); | ||
273 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
274 | spin_unlock(&gus->reg_lock); | ||
275 | if (pcmp == NULL) | ||
276 | return; | ||
277 | /* are we active? */ | ||
278 | if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) | ||
279 | return; | ||
280 | /* load real volume - better precision */ | ||
281 | cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; | ||
282 | if (pcmp->substream == NULL) | ||
283 | return; | ||
284 | vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; | ||
285 | spin_lock(&gus->reg_lock); | ||
286 | snd_gf1_select_voice(gus, pvoice->number); | ||
287 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); | ||
288 | pcmp->final_volume = 1; | ||
289 | spin_unlock(&gus->reg_lock); | ||
290 | } | ||
291 | |||
292 | static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus) | ||
293 | { | ||
294 | } | ||
295 | |||
296 | static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf, | ||
297 | unsigned int pos, unsigned int count, | ||
298 | int w16, int invert) | ||
299 | { | ||
300 | unsigned int len; | ||
301 | unsigned long flags; | ||
302 | |||
303 | // printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port); | ||
304 | while (count > 0) { | ||
305 | len = count; | ||
306 | if (len > 512) /* limit, to allow IRQ */ | ||
307 | len = 512; | ||
308 | count -= len; | ||
309 | if (gus->interwave) { | ||
310 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
311 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); | ||
312 | snd_gf1_dram_addr(gus, pos); | ||
313 | if (w16) { | ||
314 | outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); | ||
315 | outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); | ||
316 | } else { | ||
317 | outsb(GUSP(gus, DRAM), buf, len); | ||
318 | } | ||
319 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
320 | buf += 512; | ||
321 | pos += 512; | ||
322 | } else { | ||
323 | invert = invert ? 0x80 : 0x00; | ||
324 | if (w16) { | ||
325 | len >>= 1; | ||
326 | while (len--) { | ||
327 | snd_gf1_poke(gus, pos++, *buf++); | ||
328 | snd_gf1_poke(gus, pos++, *buf++ ^ invert); | ||
329 | } | ||
330 | } else { | ||
331 | while (len--) | ||
332 | snd_gf1_poke(gus, pos++, *buf++ ^ invert); | ||
333 | } | ||
334 | } | ||
335 | if (count > 0 && !in_interrupt()) { | ||
336 | set_current_state(TASK_INTERRUPTIBLE); | ||
337 | schedule_timeout(1); | ||
338 | if (signal_pending(current)) | ||
339 | return -EAGAIN; | ||
340 | } | ||
341 | } | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream, | ||
346 | int voice, | ||
347 | snd_pcm_uframes_t pos, | ||
348 | void __user *src, | ||
349 | snd_pcm_uframes_t count) | ||
350 | { | ||
351 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
352 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
353 | unsigned int bpos, len; | ||
354 | |||
355 | bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); | ||
356 | len = samples_to_bytes(runtime, count); | ||
357 | snd_assert(bpos <= pcmp->dma_size, return -EIO); | ||
358 | snd_assert(bpos + len <= pcmp->dma_size, return -EIO); | ||
359 | if (copy_from_user(runtime->dma_area + bpos, src, len)) | ||
360 | return -EFAULT; | ||
361 | if (snd_gf1_pcm_use_dma && len > 32) { | ||
362 | return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); | ||
363 | } else { | ||
364 | snd_gus_card_t *gus = pcmp->gus; | ||
365 | int err, w16, invert; | ||
366 | |||
367 | w16 = (snd_pcm_format_width(runtime->format) == 16); | ||
368 | invert = snd_pcm_format_unsigned(runtime->format); | ||
369 | if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) | ||
370 | return err; | ||
371 | } | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream, | ||
376 | int voice, | ||
377 | snd_pcm_uframes_t pos, | ||
378 | snd_pcm_uframes_t count) | ||
379 | { | ||
380 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
381 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
382 | unsigned int bpos, len; | ||
383 | |||
384 | bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); | ||
385 | len = samples_to_bytes(runtime, count); | ||
386 | snd_assert(bpos <= pcmp->dma_size, return -EIO); | ||
387 | snd_assert(bpos + len <= pcmp->dma_size, return -EIO); | ||
388 | snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count); | ||
389 | if (snd_gf1_pcm_use_dma && len > 32) { | ||
390 | return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); | ||
391 | } else { | ||
392 | snd_gus_card_t *gus = pcmp->gus; | ||
393 | int err, w16, invert; | ||
394 | |||
395 | w16 = (snd_pcm_format_width(runtime->format) == 16); | ||
396 | invert = snd_pcm_format_unsigned(runtime->format); | ||
397 | if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) | ||
398 | return err; | ||
399 | } | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream, | ||
404 | snd_pcm_hw_params_t * hw_params) | ||
405 | { | ||
406 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
407 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
408 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
409 | int err; | ||
410 | |||
411 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
412 | return err; | ||
413 | if (err > 0) { /* change */ | ||
414 | snd_gf1_mem_block_t *block; | ||
415 | if (pcmp->memory > 0) { | ||
416 | snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); | ||
417 | pcmp->memory = 0; | ||
418 | } | ||
419 | if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
420 | SNDRV_GF1_MEM_OWNER_DRIVER, | ||
421 | "GF1 PCM", | ||
422 | runtime->dma_bytes, 1, 32, | ||
423 | NULL)) == NULL) | ||
424 | return -ENOMEM; | ||
425 | pcmp->memory = block->ptr; | ||
426 | } | ||
427 | pcmp->voices = params_channels(hw_params); | ||
428 | if (pcmp->pvoices[0] == NULL) { | ||
429 | if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) | ||
430 | return -ENOMEM; | ||
431 | pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; | ||
432 | pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; | ||
433 | pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; | ||
434 | pcmp->pvoices[0]->private_data = pcmp; | ||
435 | } | ||
436 | if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { | ||
437 | if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) | ||
438 | return -ENOMEM; | ||
439 | pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; | ||
440 | pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; | ||
441 | pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; | ||
442 | pcmp->pvoices[1]->private_data = pcmp; | ||
443 | } else if (pcmp->voices == 1) { | ||
444 | if (pcmp->pvoices[1]) { | ||
445 | snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); | ||
446 | pcmp->pvoices[1] = NULL; | ||
447 | } | ||
448 | } | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream) | ||
453 | { | ||
454 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
455 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
456 | |||
457 | snd_pcm_lib_free_pages(substream); | ||
458 | if (pcmp->pvoices[0]) { | ||
459 | snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); | ||
460 | pcmp->pvoices[0] = NULL; | ||
461 | } | ||
462 | if (pcmp->pvoices[1]) { | ||
463 | snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); | ||
464 | pcmp->pvoices[1] = NULL; | ||
465 | } | ||
466 | if (pcmp->memory > 0) { | ||
467 | snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); | ||
468 | pcmp->memory = 0; | ||
469 | } | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream) | ||
474 | { | ||
475 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
476 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
477 | |||
478 | pcmp->bpos = 0; | ||
479 | pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
480 | pcmp->block_size = snd_pcm_lib_period_bytes(substream); | ||
481 | pcmp->blocks = pcmp->dma_size / pcmp->block_size; | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream, | ||
486 | int cmd) | ||
487 | { | ||
488 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
489 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
490 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
491 | int voice; | ||
492 | |||
493 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
494 | snd_gf1_pcm_trigger_up(substream); | ||
495 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
496 | spin_lock(&pcmp->lock); | ||
497 | pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; | ||
498 | spin_unlock(&pcmp->lock); | ||
499 | voice = pcmp->pvoices[0]->number; | ||
500 | snd_gf1_stop_voices(gus, voice, voice); | ||
501 | if (pcmp->pvoices[1]) { | ||
502 | voice = pcmp->pvoices[1]->number; | ||
503 | snd_gf1_stop_voices(gus, voice, voice); | ||
504 | } | ||
505 | } else { | ||
506 | return -EINVAL; | ||
507 | } | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream) | ||
512 | { | ||
513 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
514 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
515 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
516 | unsigned int pos; | ||
517 | unsigned char voice_ctrl; | ||
518 | |||
519 | pos = 0; | ||
520 | spin_lock(&gus->reg_lock); | ||
521 | if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { | ||
522 | snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); | ||
523 | voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
524 | pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; | ||
525 | if (substream->runtime->channels > 1) | ||
526 | pos <<= 1; | ||
527 | pos = bytes_to_frames(runtime, pos); | ||
528 | } | ||
529 | spin_unlock(&gus->reg_lock); | ||
530 | return pos; | ||
531 | } | ||
532 | |||
533 | static ratnum_t clock = { | ||
534 | .num = 9878400/16, | ||
535 | .den_min = 2, | ||
536 | .den_max = 257, | ||
537 | .den_step = 1, | ||
538 | }; | ||
539 | |||
540 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { | ||
541 | .nrats = 1, | ||
542 | .rats = &clock, | ||
543 | }; | ||
544 | |||
545 | static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream, | ||
546 | snd_pcm_hw_params_t * hw_params) | ||
547 | { | ||
548 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
549 | |||
550 | gus->c_dma_size = params_buffer_bytes(hw_params); | ||
551 | gus->c_period_size = params_period_bytes(hw_params); | ||
552 | gus->c_pos = 0; | ||
553 | gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ | ||
554 | if (params_channels(hw_params) > 1) | ||
555 | gus->gf1.pcm_rcntrl_reg |= 2; | ||
556 | if (gus->gf1.dma2 > 3) | ||
557 | gus->gf1.pcm_rcntrl_reg |= 4; | ||
558 | if (snd_pcm_format_unsigned(params_format(hw_params))) | ||
559 | gus->gf1.pcm_rcntrl_reg |= 0x80; | ||
560 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
561 | } | ||
562 | |||
563 | static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream) | ||
564 | { | ||
565 | return snd_pcm_lib_free_pages(substream); | ||
566 | } | ||
567 | |||
568 | static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream) | ||
569 | { | ||
570 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
571 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
572 | |||
573 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); | ||
574 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ | ||
575 | snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ | ||
576 | snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); | ||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream, | ||
581 | int cmd) | ||
582 | { | ||
583 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
584 | int val; | ||
585 | |||
586 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
587 | val = gus->gf1.pcm_rcntrl_reg; | ||
588 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
589 | val = 0; | ||
590 | } else { | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | |||
594 | spin_lock(&gus->reg_lock); | ||
595 | snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); | ||
596 | snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); | ||
597 | spin_unlock(&gus->reg_lock); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream) | ||
602 | { | ||
603 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
604 | int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size); | ||
605 | pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); | ||
606 | return pos; | ||
607 | } | ||
608 | |||
609 | static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus) | ||
610 | { | ||
611 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ | ||
612 | snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ | ||
613 | if (gus->pcm_cap_substream != NULL) { | ||
614 | snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); | ||
615 | snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); | ||
616 | gus->c_pos += gus->c_period_size; | ||
617 | snd_pcm_period_elapsed(gus->pcm_cap_substream); | ||
618 | } | ||
619 | } | ||
620 | |||
621 | static snd_pcm_hardware_t snd_gf1_pcm_playback = | ||
622 | { | ||
623 | .info = SNDRV_PCM_INFO_NONINTERLEAVED, | ||
624 | .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | | ||
625 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), | ||
626 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
627 | .rate_min = 5510, | ||
628 | .rate_max = 48000, | ||
629 | .channels_min = 1, | ||
630 | .channels_max = 2, | ||
631 | .buffer_bytes_max = (128*1024), | ||
632 | .period_bytes_min = 64, | ||
633 | .period_bytes_max = (128*1024), | ||
634 | .periods_min = 1, | ||
635 | .periods_max = 1024, | ||
636 | .fifo_size = 0, | ||
637 | }; | ||
638 | |||
639 | static snd_pcm_hardware_t snd_gf1_pcm_capture = | ||
640 | { | ||
641 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
642 | SNDRV_PCM_INFO_MMAP_VALID), | ||
643 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, | ||
644 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, | ||
645 | .rate_min = 5510, | ||
646 | .rate_max = 44100, | ||
647 | .channels_min = 1, | ||
648 | .channels_max = 2, | ||
649 | .buffer_bytes_max = (128*1024), | ||
650 | .period_bytes_min = 64, | ||
651 | .period_bytes_max = (128*1024), | ||
652 | .periods_min = 1, | ||
653 | .periods_max = 1024, | ||
654 | .fifo_size = 0, | ||
655 | }; | ||
656 | |||
657 | static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime) | ||
658 | { | ||
659 | gus_pcm_private_t * pcmp = runtime->private_data; | ||
660 | kfree(pcmp); | ||
661 | } | ||
662 | |||
663 | static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream) | ||
664 | { | ||
665 | gus_pcm_private_t *pcmp; | ||
666 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
667 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
668 | int err; | ||
669 | |||
670 | pcmp = kcalloc(1, sizeof(*pcmp), GFP_KERNEL); | ||
671 | if (pcmp == NULL) | ||
672 | return -ENOMEM; | ||
673 | pcmp->gus = gus; | ||
674 | spin_lock_init(&pcmp->lock); | ||
675 | init_waitqueue_head(&pcmp->sleep); | ||
676 | atomic_set(&pcmp->dma_count, 0); | ||
677 | |||
678 | runtime->private_data = pcmp; | ||
679 | runtime->private_free = snd_gf1_pcm_playback_free; | ||
680 | |||
681 | #if 0 | ||
682 | printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); | ||
683 | #endif | ||
684 | if ((err = snd_gf1_dma_init(gus)) < 0) | ||
685 | return err; | ||
686 | pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; | ||
687 | pcmp->substream = substream; | ||
688 | runtime->hw = snd_gf1_pcm_playback; | ||
689 | snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); | ||
690 | snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); | ||
691 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); | ||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream) | ||
696 | { | ||
697 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
698 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
699 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
700 | |||
701 | if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ)) | ||
702 | snd_printk("gf1 pcm - serious DMA problem\n"); | ||
703 | |||
704 | snd_gf1_dma_done(gus); | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream) | ||
709 | { | ||
710 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
711 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
712 | |||
713 | gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; | ||
714 | gus->pcm_cap_substream = substream; | ||
715 | substream->runtime->hw = snd_gf1_pcm_capture; | ||
716 | snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); | ||
717 | snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); | ||
718 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
719 | &hw_constraints_clocks); | ||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream) | ||
724 | { | ||
725 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
726 | |||
727 | gus->pcm_cap_substream = NULL; | ||
728 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static void snd_gf1_pcm_free(snd_pcm_t *pcm) | ||
733 | { | ||
734 | snd_gus_card_t *gus = pcm->private_data; | ||
735 | gus->pcm = NULL; | ||
736 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
737 | } | ||
738 | |||
739 | static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
740 | { | ||
741 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
742 | uinfo->count = 2; | ||
743 | uinfo->value.integer.min = 0; | ||
744 | uinfo->value.integer.max = 127; | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
749 | { | ||
750 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
751 | unsigned long flags; | ||
752 | |||
753 | spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); | ||
754 | ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; | ||
755 | ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; | ||
756 | spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
761 | { | ||
762 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
763 | unsigned long flags; | ||
764 | int change; | ||
765 | unsigned int idx; | ||
766 | unsigned short val1, val2, vol; | ||
767 | gus_pcm_private_t *pcmp; | ||
768 | snd_gus_voice_t *pvoice; | ||
769 | |||
770 | val1 = ucontrol->value.integer.value[0] & 127; | ||
771 | val2 = ucontrol->value.integer.value[1] & 127; | ||
772 | spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); | ||
773 | change = val1 != gus->gf1.pcm_volume_level_left1 || | ||
774 | val2 != gus->gf1.pcm_volume_level_right1; | ||
775 | gus->gf1.pcm_volume_level_left1 = val1; | ||
776 | gus->gf1.pcm_volume_level_right1 = val2; | ||
777 | gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; | ||
778 | gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; | ||
779 | spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); | ||
780 | /* are we active? */ | ||
781 | spin_lock_irqsave(&gus->voice_alloc, flags); | ||
782 | for (idx = 0; idx < 32; idx++) { | ||
783 | pvoice = &gus->gf1.voices[idx]; | ||
784 | if (!pvoice->pcm) | ||
785 | continue; | ||
786 | pcmp = pvoice->private_data; | ||
787 | if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) | ||
788 | continue; | ||
789 | /* load real volume - better precision */ | ||
790 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
791 | snd_gf1_select_voice(gus, pvoice->number); | ||
792 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
793 | vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; | ||
794 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); | ||
795 | pcmp->final_volume = 1; | ||
796 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
797 | } | ||
798 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
799 | return change; | ||
800 | } | ||
801 | |||
802 | static snd_kcontrol_new_t snd_gf1_pcm_volume_control = | ||
803 | { | ||
804 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
805 | .name = "PCM Playback Volume", | ||
806 | .info = snd_gf1_pcm_volume_info, | ||
807 | .get = snd_gf1_pcm_volume_get, | ||
808 | .put = snd_gf1_pcm_volume_put | ||
809 | }; | ||
810 | |||
811 | static snd_kcontrol_new_t snd_gf1_pcm_volume_control1 = | ||
812 | { | ||
813 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
814 | .name = "GPCM Playback Volume", | ||
815 | .info = snd_gf1_pcm_volume_info, | ||
816 | .get = snd_gf1_pcm_volume_get, | ||
817 | .put = snd_gf1_pcm_volume_put | ||
818 | }; | ||
819 | |||
820 | static snd_pcm_ops_t snd_gf1_pcm_playback_ops = { | ||
821 | .open = snd_gf1_pcm_playback_open, | ||
822 | .close = snd_gf1_pcm_playback_close, | ||
823 | .ioctl = snd_pcm_lib_ioctl, | ||
824 | .hw_params = snd_gf1_pcm_playback_hw_params, | ||
825 | .hw_free = snd_gf1_pcm_playback_hw_free, | ||
826 | .prepare = snd_gf1_pcm_playback_prepare, | ||
827 | .trigger = snd_gf1_pcm_playback_trigger, | ||
828 | .pointer = snd_gf1_pcm_playback_pointer, | ||
829 | .copy = snd_gf1_pcm_playback_copy, | ||
830 | .silence = snd_gf1_pcm_playback_silence, | ||
831 | }; | ||
832 | |||
833 | static snd_pcm_ops_t snd_gf1_pcm_capture_ops = { | ||
834 | .open = snd_gf1_pcm_capture_open, | ||
835 | .close = snd_gf1_pcm_capture_close, | ||
836 | .ioctl = snd_pcm_lib_ioctl, | ||
837 | .hw_params = snd_gf1_pcm_capture_hw_params, | ||
838 | .hw_free = snd_gf1_pcm_capture_hw_free, | ||
839 | .prepare = snd_gf1_pcm_capture_prepare, | ||
840 | .trigger = snd_gf1_pcm_capture_trigger, | ||
841 | .pointer = snd_gf1_pcm_capture_pointer, | ||
842 | }; | ||
843 | |||
844 | int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm) | ||
845 | { | ||
846 | snd_card_t *card; | ||
847 | snd_kcontrol_t *kctl; | ||
848 | snd_pcm_t *pcm; | ||
849 | snd_pcm_substream_t *substream; | ||
850 | int capture, err; | ||
851 | |||
852 | if (rpcm) | ||
853 | *rpcm = NULL; | ||
854 | card = gus->card; | ||
855 | capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; | ||
856 | err = snd_pcm_new(card, | ||
857 | gus->interwave ? "AMD InterWave" : "GF1", | ||
858 | pcm_dev, | ||
859 | gus->gf1.pcm_channels / 2, | ||
860 | capture, | ||
861 | &pcm); | ||
862 | if (err < 0) | ||
863 | return err; | ||
864 | pcm->private_data = gus; | ||
865 | pcm->private_free = snd_gf1_pcm_free; | ||
866 | /* playback setup */ | ||
867 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); | ||
868 | |||
869 | for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) | ||
870 | snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, | ||
871 | snd_dma_isa_data(), | ||
872 | 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); | ||
873 | |||
874 | pcm->info_flags = 0; | ||
875 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | ||
876 | if (capture) { | ||
877 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); | ||
878 | if (gus->gf1.dma2 == gus->gf1.dma1) | ||
879 | pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; | ||
880 | snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, | ||
881 | SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), | ||
882 | 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); | ||
883 | } | ||
884 | strcpy(pcm->name, pcm->id); | ||
885 | if (gus->interwave) { | ||
886 | sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); | ||
887 | } | ||
888 | strcat(pcm->name, " (synth)"); | ||
889 | gus->pcm = pcm; | ||
890 | |||
891 | if (gus->codec_flag) | ||
892 | kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); | ||
893 | else | ||
894 | kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); | ||
895 | if ((err = snd_ctl_add(card, kctl)) < 0) | ||
896 | return err; | ||
897 | kctl->id.index = control_index; | ||
898 | |||
899 | if (rpcm) | ||
900 | *rpcm = pcm; | ||
901 | return 0; | ||
902 | } | ||
903 | |||
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c new file mode 100644 index 000000000000..b4e66f6a10ae --- /dev/null +++ b/sound/isa/gus/gus_reset.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | |||
28 | extern void snd_gf1_timers_init(snd_gus_card_t * gus); | ||
29 | extern void snd_gf1_timers_done(snd_gus_card_t * gus); | ||
30 | extern int snd_gf1_synth_init(snd_gus_card_t * gus); | ||
31 | extern void snd_gf1_synth_done(snd_gus_card_t * gus); | ||
32 | |||
33 | /* | ||
34 | * ok.. default interrupt handlers... | ||
35 | */ | ||
36 | |||
37 | static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus) | ||
38 | { | ||
39 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20); | ||
40 | } | ||
41 | |||
42 | static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus) | ||
43 | { | ||
44 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80); | ||
45 | } | ||
46 | |||
47 | static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus) | ||
48 | { | ||
49 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4); | ||
50 | } | ||
51 | |||
52 | static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus) | ||
53 | { | ||
54 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8); | ||
55 | } | ||
56 | |||
57 | static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice) | ||
58 | { | ||
59 | snd_gf1_i_ctrl_stop(gus, 0x00); | ||
60 | snd_gf1_i_ctrl_stop(gus, 0x0d); | ||
61 | } | ||
62 | |||
63 | static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus) | ||
64 | { | ||
65 | snd_gf1_i_write8(gus, 0x41, 0x00); | ||
66 | } | ||
67 | |||
68 | static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus) | ||
69 | { | ||
70 | snd_gf1_i_write8(gus, 0x49, 0x00); | ||
71 | } | ||
72 | |||
73 | void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what) | ||
74 | { | ||
75 | if (what & SNDRV_GF1_HANDLER_MIDI_OUT) | ||
76 | gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out; | ||
77 | if (what & SNDRV_GF1_HANDLER_MIDI_IN) | ||
78 | gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in; | ||
79 | if (what & SNDRV_GF1_HANDLER_TIMER1) | ||
80 | gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1; | ||
81 | if (what & SNDRV_GF1_HANDLER_TIMER2) | ||
82 | gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2; | ||
83 | if (what & SNDRV_GF1_HANDLER_VOICE) { | ||
84 | snd_gus_voice_t *voice; | ||
85 | |||
86 | voice = &gus->gf1.voices[what & 0xffff]; | ||
87 | voice->handler_wave = | ||
88 | voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume; | ||
89 | voice->handler_effect = NULL; | ||
90 | voice->volume_change = NULL; | ||
91 | } | ||
92 | if (what & SNDRV_GF1_HANDLER_DMA_WRITE) | ||
93 | gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write; | ||
94 | if (what & SNDRV_GF1_HANDLER_DMA_READ) | ||
95 | gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | |||
100 | */ | ||
101 | |||
102 | static void snd_gf1_clear_regs(snd_gus_card_t * gus) | ||
103 | { | ||
104 | unsigned long flags; | ||
105 | |||
106 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
107 | inb(GUSP(gus, IRQSTAT)); | ||
108 | snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */ | ||
109 | snd_gf1_write8(gus, 0x45, 0); /* Timer Control */ | ||
110 | snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */ | ||
111 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
112 | } | ||
113 | |||
114 | static void snd_gf1_look_regs(snd_gus_card_t * gus) | ||
115 | { | ||
116 | unsigned long flags; | ||
117 | |||
118 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
119 | snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */ | ||
120 | snd_gf1_look8(gus, 0x49); /* Sampling Control Register */ | ||
121 | inb(GUSP(gus, IRQSTAT)); | ||
122 | snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */ | ||
123 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * put selected GF1 voices to initial stage... | ||
128 | */ | ||
129 | |||
130 | void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice) | ||
131 | { | ||
132 | unsigned long flags; | ||
133 | |||
134 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
135 | snd_gf1_select_voice(gus, voice); | ||
136 | #if 0 | ||
137 | printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); | ||
138 | #endif | ||
139 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
140 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
141 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
142 | } | ||
143 | |||
144 | void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice) | ||
145 | { | ||
146 | unsigned long flags; | ||
147 | |||
148 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
149 | snd_gf1_select_voice(gus, voice); | ||
150 | #if 0 | ||
151 | printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); | ||
152 | #endif | ||
153 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
154 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
155 | if (gus->gf1.enh_mode) | ||
156 | snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); | ||
157 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
158 | #if 0 | ||
159 | snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO); | ||
160 | snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO); | ||
161 | #endif | ||
162 | } | ||
163 | |||
164 | void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) | ||
165 | { | ||
166 | unsigned long flags; | ||
167 | unsigned int daddr; | ||
168 | unsigned short i, w_16; | ||
169 | |||
170 | daddr = gus->gf1.default_voice_address << 4; | ||
171 | for (i = v_min; i <= v_max; i++) { | ||
172 | #if 0 | ||
173 | if (gus->gf1.syn_voices) | ||
174 | gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC; | ||
175 | #endif | ||
176 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
177 | snd_gf1_select_voice(gus, i); | ||
178 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */ | ||
179 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */ | ||
180 | if (gus->gf1.enh_mode) | ||
181 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82); /* Deactivate voice */ | ||
182 | w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04; | ||
183 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400); | ||
184 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16); | ||
185 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16); | ||
186 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0); | ||
187 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0); | ||
188 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0); | ||
189 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); | ||
190 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16); | ||
191 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7); | ||
192 | if (gus->gf1.enh_mode) { | ||
193 | snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); | ||
194 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0); | ||
195 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0); | ||
196 | } | ||
197 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
198 | #if 0 | ||
199 | snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO); | ||
200 | snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO); | ||
201 | #endif | ||
202 | } | ||
203 | } | ||
204 | |||
205 | void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) | ||
206 | { | ||
207 | unsigned long flags; | ||
208 | short i, ramp_ok; | ||
209 | unsigned short ramp_end; | ||
210 | |||
211 | if (!in_interrupt()) { /* this can't be done in interrupt */ | ||
212 | for (i = v_min, ramp_ok = 0; i <= v_max; i++) { | ||
213 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
214 | snd_gf1_select_voice(gus, i); | ||
215 | ramp_end = snd_gf1_read16(gus, 9) >> 8; | ||
216 | if (ramp_end > SNDRV_GF1_MIN_OFFSET) { | ||
217 | ramp_ok++; | ||
218 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20); /* ramp rate */ | ||
219 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); /* ramp start */ | ||
220 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end); /* ramp end */ | ||
221 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); /* ramp down */ | ||
222 | if (gus->gf1.enh_mode) { | ||
223 | snd_gf1_delay(gus); | ||
224 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); | ||
225 | } | ||
226 | } | ||
227 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
228 | } | ||
229 | msleep_interruptible(50); | ||
230 | } | ||
231 | snd_gf1_clear_voices(gus, v_min, v_max); | ||
232 | } | ||
233 | |||
234 | static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, | ||
235 | snd_gus_voice_t * pvoice, | ||
236 | int type, int client, int port) | ||
237 | { | ||
238 | pvoice->use = 1; | ||
239 | switch (type) { | ||
240 | case SNDRV_GF1_VOICE_TYPE_PCM: | ||
241 | gus->gf1.pcm_alloc_voices++; | ||
242 | pvoice->pcm = 1; | ||
243 | break; | ||
244 | case SNDRV_GF1_VOICE_TYPE_SYNTH: | ||
245 | pvoice->synth = 1; | ||
246 | pvoice->client = client; | ||
247 | pvoice->port = port; | ||
248 | break; | ||
249 | case SNDRV_GF1_VOICE_TYPE_MIDI: | ||
250 | pvoice->midi = 1; | ||
251 | pvoice->client = client; | ||
252 | pvoice->port = port; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port) | ||
258 | { | ||
259 | snd_gus_voice_t *pvoice; | ||
260 | unsigned long flags; | ||
261 | int idx; | ||
262 | |||
263 | spin_lock_irqsave(&gus->voice_alloc, flags); | ||
264 | if (type == SNDRV_GF1_VOICE_TYPE_PCM) { | ||
265 | if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) { | ||
266 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
267 | return NULL; | ||
268 | } | ||
269 | } | ||
270 | for (idx = 0; idx < 32; idx++) { | ||
271 | pvoice = &gus->gf1.voices[idx]; | ||
272 | if (!pvoice->use) { | ||
273 | snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); | ||
274 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
275 | return pvoice; | ||
276 | } | ||
277 | } | ||
278 | for (idx = 0; idx < 32; idx++) { | ||
279 | pvoice = &gus->gf1.voices[idx]; | ||
280 | if (pvoice->midi && !pvoice->client) { | ||
281 | snd_gf1_clear_voices(gus, pvoice->number, pvoice->number); | ||
282 | snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); | ||
283 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
284 | return pvoice; | ||
285 | } | ||
286 | } | ||
287 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
288 | return NULL; | ||
289 | } | ||
290 | |||
291 | void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice) | ||
292 | { | ||
293 | unsigned long flags; | ||
294 | void (*private_free)(snd_gus_voice_t *voice); | ||
295 | void *private_data; | ||
296 | |||
297 | if (voice == NULL || !voice->use) | ||
298 | return; | ||
299 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number); | ||
300 | snd_gf1_clear_voices(gus, voice->number, voice->number); | ||
301 | spin_lock_irqsave(&gus->voice_alloc, flags); | ||
302 | private_free = voice->private_free; | ||
303 | private_data = voice->private_data; | ||
304 | voice->private_free = NULL; | ||
305 | voice->private_data = NULL; | ||
306 | if (voice->pcm) | ||
307 | gus->gf1.pcm_alloc_voices--; | ||
308 | voice->use = voice->pcm = 0; | ||
309 | voice->sample_ops = NULL; | ||
310 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
311 | if (private_free) | ||
312 | private_free(voice); | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * call this function only by start of driver | ||
317 | */ | ||
318 | |||
319 | int snd_gf1_start(snd_gus_card_t * gus) | ||
320 | { | ||
321 | unsigned long flags; | ||
322 | unsigned int i; | ||
323 | |||
324 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
325 | udelay(160); | ||
326 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ | ||
327 | udelay(160); | ||
328 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); | ||
329 | |||
330 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); | ||
331 | for (i = 0; i < 32; i++) { | ||
332 | gus->gf1.voices[i].number = i; | ||
333 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); | ||
334 | } | ||
335 | |||
336 | snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ | ||
337 | |||
338 | if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ | ||
339 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); | ||
340 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
341 | } | ||
342 | snd_gf1_clear_regs(gus); | ||
343 | snd_gf1_select_active_voices(gus); | ||
344 | snd_gf1_delay(gus); | ||
345 | gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; | ||
346 | /* initialize LFOs & clear LFOs memory */ | ||
347 | if (gus->gf1.enh_mode && gus->gf1.memory) { | ||
348 | gus->gf1.hw_lfo = 1; | ||
349 | gus->gf1.default_voice_address += 1024; | ||
350 | } else { | ||
351 | gus->gf1.sw_lfo = 1; | ||
352 | } | ||
353 | #if 0 | ||
354 | snd_gf1_lfo_init(gus); | ||
355 | #endif | ||
356 | if (gus->gf1.memory > 0) | ||
357 | for (i = 0; i < 4; i++) | ||
358 | snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0); | ||
359 | snd_gf1_clear_regs(gus); | ||
360 | snd_gf1_clear_voices(gus, 0, 31); | ||
361 | snd_gf1_look_regs(gus); | ||
362 | udelay(160); | ||
363 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ | ||
364 | udelay(160); | ||
365 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ | ||
366 | if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ | ||
367 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); | ||
368 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
369 | } | ||
370 | while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0); | ||
371 | |||
372 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
373 | outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); | ||
374 | outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
375 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
376 | |||
377 | snd_gf1_timers_init(gus); | ||
378 | snd_gf1_look_regs(gus); | ||
379 | snd_gf1_mem_init(gus); | ||
380 | snd_gf1_mem_proc_init(gus); | ||
381 | #ifdef CONFIG_SND_DEBUG | ||
382 | snd_gus_irq_profile_init(gus); | ||
383 | #endif | ||
384 | |||
385 | #if 0 | ||
386 | if (gus->pnp_flag) { | ||
387 | if (gus->chip.playback_fifo_size > 0) | ||
388 | snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8); | ||
389 | if (gus->chip.record_fifo_size > 0) | ||
390 | snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8); | ||
391 | snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg); | ||
392 | } | ||
393 | #endif | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * call this function only by shutdown of driver | ||
400 | */ | ||
401 | |||
402 | int snd_gf1_stop(snd_gus_card_t * gus) | ||
403 | { | ||
404 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */ | ||
405 | snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ | ||
406 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ | ||
407 | snd_gf1_timers_done(gus); | ||
408 | snd_gf1_mem_done(gus); | ||
409 | #if 0 | ||
410 | snd_gf1_lfo_done(gus); | ||
411 | #endif | ||
412 | return 0; | ||
413 | } | ||
diff --git a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c new file mode 100644 index 000000000000..4290e03acd51 --- /dev/null +++ b/sound/isa/gus/gus_sample.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Sample support | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | |||
27 | /* | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v) | ||
32 | { | ||
33 | snd_seq_kinstr_t *instr; | ||
34 | |||
35 | #if 0 | ||
36 | printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n", | ||
37 | v->instr.cluster, | ||
38 | v->instr.std, | ||
39 | v->instr.bank, | ||
40 | v->instr.prg); | ||
41 | #endif | ||
42 | instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1); | ||
43 | if (instr != NULL) { | ||
44 | if (instr->ops) { | ||
45 | if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE)) | ||
46 | snd_gf1_simple_init(v); | ||
47 | } | ||
48 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * | ||
54 | */ | ||
55 | |||
56 | static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
57 | { | ||
58 | if (v->sample_ops && v->sample_ops->sample_stop) | ||
59 | v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); | ||
60 | v->instr.std = ev->data.sample.param.sample.std; | ||
61 | if (v->instr.std & 0xff000000) { /* private instrument */ | ||
62 | v->instr.std &= 0x00ffffff; | ||
63 | v->instr.std |= (unsigned int)ev->source.client << 24; | ||
64 | } | ||
65 | v->instr.bank = ev->data.sample.param.sample.bank; | ||
66 | v->instr.prg = ev->data.sample.param.sample.prg; | ||
67 | select_instrument(p->gus, v); | ||
68 | } | ||
69 | |||
70 | static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
71 | { | ||
72 | if (v->sample_ops && v->sample_ops->sample_stop) | ||
73 | v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); | ||
74 | v->instr.cluster = ev->data.sample.param.cluster.cluster; | ||
75 | select_instrument(p->gus, v); | ||
76 | } | ||
77 | |||
78 | static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
79 | { | ||
80 | if (v->sample_ops && v->sample_ops->sample_start) | ||
81 | v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position); | ||
82 | } | ||
83 | |||
84 | static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
85 | { | ||
86 | if (v->sample_ops && v->sample_ops->sample_stop) | ||
87 | v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode); | ||
88 | } | ||
89 | |||
90 | static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
91 | { | ||
92 | if (v->sample_ops && v->sample_ops->sample_freq) | ||
93 | v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency); | ||
94 | } | ||
95 | |||
96 | static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
97 | { | ||
98 | if (v->sample_ops && v->sample_ops->sample_volume) | ||
99 | v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume); | ||
100 | } | ||
101 | |||
102 | static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
103 | { | ||
104 | if (v->sample_ops && v->sample_ops->sample_loop) | ||
105 | v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop); | ||
106 | } | ||
107 | |||
108 | static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
109 | { | ||
110 | if (v->sample_ops && v->sample_ops->sample_pos) | ||
111 | v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position); | ||
112 | } | ||
113 | |||
114 | static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
115 | { | ||
116 | if (v->sample_ops && v->sample_ops->sample_private1) | ||
117 | v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8); | ||
118 | } | ||
119 | |||
120 | typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v); | ||
121 | |||
122 | static gus_sample_event_handler_t *gus_sample_event_handlers[9] = { | ||
123 | event_sample, | ||
124 | event_cluster, | ||
125 | event_start, | ||
126 | event_stop, | ||
127 | event_freq, | ||
128 | event_volume, | ||
129 | event_loop, | ||
130 | event_position, | ||
131 | event_private1 | ||
132 | }; | ||
133 | |||
134 | void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p) | ||
135 | { | ||
136 | int idx, voice; | ||
137 | snd_gus_card_t *gus = p->gus; | ||
138 | snd_gus_voice_t *v; | ||
139 | unsigned long flags; | ||
140 | |||
141 | idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; | ||
142 | if (idx < 0 || idx > 8) | ||
143 | return; | ||
144 | for (voice = 0; voice < 32; voice++) { | ||
145 | v = &gus->gf1.voices[voice]; | ||
146 | if (v->use && v->client == ev->source.client && | ||
147 | v->port == ev->source.port && | ||
148 | v->index == ev->data.sample.channel) { | ||
149 | spin_lock_irqsave(&gus->event_lock, flags); | ||
150 | gus_sample_event_handlers[idx](ev, p, v); | ||
151 | spin_unlock_irqrestore(&gus->event_lock, flags); | ||
152 | return; | ||
153 | } | ||
154 | } | ||
155 | } | ||
diff --git a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c new file mode 100644 index 000000000000..c122e7be6ceb --- /dev/null +++ b/sound/isa/gus/gus_simple.c | |||
@@ -0,0 +1,634 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Simple instrument handlers | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include "gus_tables.h" | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
33 | static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
34 | static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
35 | |||
36 | static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); | ||
37 | static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); | ||
38 | static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); | ||
39 | static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); | ||
40 | static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); | ||
41 | static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); | ||
42 | static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); | ||
43 | |||
44 | static snd_gus_sample_ops_t sample_ops = { | ||
45 | sample_start, | ||
46 | sample_stop, | ||
47 | sample_freq, | ||
48 | sample_volume, | ||
49 | sample_loop, | ||
50 | sample_pos, | ||
51 | sample_private1 | ||
52 | }; | ||
53 | |||
54 | #if 0 | ||
55 | |||
56 | static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait); | ||
57 | static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
58 | static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
59 | static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
60 | static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
61 | static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
62 | static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
63 | |||
64 | static struct snd_gus_note_handlers note_commands = { | ||
65 | note_stop, | ||
66 | note_wait, | ||
67 | note_off, | ||
68 | note_volume, | ||
69 | note_pitchbend, | ||
70 | note_vibrato, | ||
71 | note_tremolo | ||
72 | }; | ||
73 | |||
74 | static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ); | ||
75 | static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ); | ||
76 | static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ); | ||
77 | |||
78 | static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = { | ||
79 | chn_trigger_down, | ||
80 | chn_trigger_up, | ||
81 | chn_control | ||
82 | }; | ||
83 | |||
84 | #endif | ||
85 | |||
86 | static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
87 | static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
88 | |||
89 | /* | ||
90 | * | ||
91 | */ | ||
92 | |||
93 | static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
94 | { | ||
95 | spin_lock(&gus->event_lock); | ||
96 | snd_gf1_stop_voice(gus, voice->number); | ||
97 | spin_lock(&gus->reg_lock); | ||
98 | snd_gf1_select_voice(gus, voice->number); | ||
99 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); | ||
100 | spin_unlock(&gus->reg_lock); | ||
101 | voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
102 | spin_unlock(&gus->event_lock); | ||
103 | } | ||
104 | |||
105 | static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
106 | { | ||
107 | spin_lock(&gus->event_lock); | ||
108 | if (voice->flags & SNDRV_GF1_VFLG_RUNNING) | ||
109 | do_volume_envelope(gus, voice); | ||
110 | else | ||
111 | snd_gf1_stop_voice(gus, voice->number); | ||
112 | spin_unlock(&gus->event_lock); | ||
113 | } | ||
114 | |||
115 | static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
116 | { | ||
117 | spin_lock(&gus->event_lock); | ||
118 | if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) == | ||
119 | (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) | ||
120 | do_pan_envelope(gus, voice); | ||
121 | spin_unlock(&gus->event_lock); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * | ||
126 | */ | ||
127 | |||
128 | static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
129 | { | ||
130 | unsigned short next, rate, old_volume; | ||
131 | int program_next_ramp; | ||
132 | unsigned long flags; | ||
133 | |||
134 | if (!gus->gf1.volume_ramp) { | ||
135 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
136 | snd_gf1_select_voice(gus, voice->number); | ||
137 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
138 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume); | ||
139 | printk("gf1_volume = 0x%x\n", voice->gf1_volume); | ||
140 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
141 | return; | ||
142 | } | ||
143 | program_next_ramp = 0; | ||
144 | rate = next = 0; | ||
145 | while (1) { | ||
146 | program_next_ramp = 0; | ||
147 | rate = next = 0; | ||
148 | switch (voice->venv_state) { | ||
149 | case VENV_BEFORE: | ||
150 | voice->venv_state = VENV_ATTACK; | ||
151 | voice->venv_value_next = 0; | ||
152 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
153 | snd_gf1_select_voice(gus, voice->number); | ||
154 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
155 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); | ||
156 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
157 | break; | ||
158 | case VENV_ATTACK: | ||
159 | voice->venv_state = VENV_SUSTAIN; | ||
160 | program_next_ramp++; | ||
161 | next = 255; | ||
162 | rate = gus->gf1.volume_ramp; | ||
163 | break; | ||
164 | case VENV_SUSTAIN: | ||
165 | voice->venv_state = VENV_RELEASE; | ||
166 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
167 | snd_gf1_select_voice(gus, voice->number); | ||
168 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
169 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255); | ||
170 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
171 | return; | ||
172 | case VENV_RELEASE: | ||
173 | voice->venv_state = VENV_DONE; | ||
174 | program_next_ramp++; | ||
175 | next = 0; | ||
176 | rate = gus->gf1.volume_ramp; | ||
177 | break; | ||
178 | case VENV_DONE: | ||
179 | snd_gf1_stop_voice(gus, voice->number); | ||
180 | voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
181 | return; | ||
182 | case VENV_VOLUME: | ||
183 | program_next_ramp++; | ||
184 | next = voice->venv_value_next; | ||
185 | rate = gus->gf1.volume_ramp; | ||
186 | voice->venv_state = voice->venv_state_prev; | ||
187 | break; | ||
188 | } | ||
189 | voice->venv_value_next = next; | ||
190 | if (!program_next_ramp) | ||
191 | continue; | ||
192 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
193 | snd_gf1_select_voice(gus, voice->number); | ||
194 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
195 | old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8; | ||
196 | if (!rate) { | ||
197 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
198 | continue; | ||
199 | } | ||
200 | next = (((int)voice->gf1_volume * (int)next) / 255) >> 8; | ||
201 | if (old_volume < SNDRV_GF1_MIN_OFFSET) | ||
202 | old_volume = SNDRV_GF1_MIN_OFFSET; | ||
203 | if (next < SNDRV_GF1_MIN_OFFSET) | ||
204 | next = SNDRV_GF1_MIN_OFFSET; | ||
205 | if (next > SNDRV_GF1_MAX_OFFSET) | ||
206 | next = SNDRV_GF1_MAX_OFFSET; | ||
207 | if (old_volume == next) { | ||
208 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
209 | continue; | ||
210 | } | ||
211 | voice->volume_control &= ~0xc3; | ||
212 | voice->volume_control |= 0x20; | ||
213 | if (old_volume > next) { | ||
214 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next); | ||
215 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume); | ||
216 | voice->volume_control |= 0x40; | ||
217 | } else { | ||
218 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume); | ||
219 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next); | ||
220 | } | ||
221 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate); | ||
222 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); | ||
223 | if (!gus->gf1.enh_mode) { | ||
224 | snd_gf1_delay(gus); | ||
225 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); | ||
226 | } | ||
227 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
228 | return; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
233 | { | ||
234 | unsigned long flags; | ||
235 | unsigned char old_pan; | ||
236 | |||
237 | #if 0 | ||
238 | snd_gf1_select_voice(gus, voice->number); | ||
239 | printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", | ||
240 | voice->number, | ||
241 | voice->flags, | ||
242 | voice->gf1_pan, | ||
243 | snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f); | ||
244 | #endif | ||
245 | if (gus->gf1.enh_mode) { | ||
246 | voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); | ||
247 | return; | ||
248 | } | ||
249 | if (!gus->gf1.smooth_pan) { | ||
250 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
251 | snd_gf1_select_voice(gus, voice->number); | ||
252 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); | ||
253 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
254 | return; | ||
255 | } | ||
256 | if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */ | ||
257 | voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN; | ||
258 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
259 | snd_gf1_select_voice(gus, voice->number); | ||
260 | old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f; | ||
261 | if (old_pan > voice->gf1_pan ) | ||
262 | old_pan--; | ||
263 | if (old_pan < voice->gf1_pan) | ||
264 | old_pan++; | ||
265 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan); | ||
266 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
267 | if (old_pan == voice->gf1_pan) /* the goal was reached */ | ||
268 | voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); | ||
269 | #if 0 | ||
270 | snd_gf1_select_voice(gus, voice->number); | ||
271 | printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", | ||
272 | voice->number, | ||
273 | voice->flags, | ||
274 | voice->gf1_pan, | ||
275 | snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f); | ||
276 | #endif | ||
277 | } | ||
278 | |||
279 | static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan) | ||
280 | { | ||
281 | unsigned long flags; | ||
282 | unsigned short vlo, vro; | ||
283 | |||
284 | vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan); | ||
285 | vro = SNDRV_GF1_ATTEN(pan); | ||
286 | if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) { | ||
287 | vlo >>= 1; | ||
288 | vro >>= 1; | ||
289 | } | ||
290 | vlo <<= 4; | ||
291 | vro <<= 4; | ||
292 | #if 0 | ||
293 | printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n", | ||
294 | vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT), | ||
295 | vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT)); | ||
296 | #endif | ||
297 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
298 | snd_gf1_select_voice(gus, voice->number); | ||
299 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo); | ||
300 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro); | ||
301 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
302 | voice->vlo = vlo; | ||
303 | voice->vro = vro; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * | ||
308 | */ | ||
309 | |||
310 | static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) | ||
311 | { | ||
312 | unsigned long flags; | ||
313 | unsigned int begin, addr, addr_end, addr_start; | ||
314 | int w_16; | ||
315 | simple_instrument_t *simple; | ||
316 | snd_seq_kinstr_t *instr; | ||
317 | |||
318 | instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); | ||
319 | if (instr == NULL) | ||
320 | return; | ||
321 | voice->instr = instr->instr; /* copy ID to speedup aliases */ | ||
322 | simple = KINSTR_DATA(instr); | ||
323 | begin = simple->address.memory << 4; | ||
324 | w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0; | ||
325 | addr_start = simple->loop_start; | ||
326 | if (simple->format & SIMPLE_WAVE_LOOP) { | ||
327 | addr_end = simple->loop_end; | ||
328 | } else { | ||
329 | addr_end = (simple->size << 4) - (w_16 ? 40 : 24); | ||
330 | } | ||
331 | if (simple->format & SIMPLE_WAVE_BACKWARD) { | ||
332 | addr = simple->loop_end; | ||
333 | if (position < simple->loop_end) | ||
334 | addr -= position; | ||
335 | } else { | ||
336 | addr = position; | ||
337 | } | ||
338 | voice->control = 0x00; | ||
339 | voice->mode = 0x20; /* enable offset registers */ | ||
340 | if (simple->format & SIMPLE_WAVE_16BIT) | ||
341 | voice->control |= 0x04; | ||
342 | if (simple->format & SIMPLE_WAVE_BACKWARD) | ||
343 | voice->control |= 0x40; | ||
344 | if (simple->format & SIMPLE_WAVE_LOOP) { | ||
345 | voice->control |= 0x08; | ||
346 | } else { | ||
347 | voice->control |= 0x20; | ||
348 | } | ||
349 | if (simple->format & SIMPLE_WAVE_BIDIR) | ||
350 | voice->control |= 0x10; | ||
351 | if (simple->format & SIMPLE_WAVE_ULAW) | ||
352 | voice->mode |= 0x40; | ||
353 | if (w_16) { | ||
354 | addr = ((addr << 1) & ~0x1f) | (addr & 0x0f); | ||
355 | addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f); | ||
356 | addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f); | ||
357 | } | ||
358 | addr += begin; | ||
359 | addr_start += begin; | ||
360 | addr_end += begin; | ||
361 | snd_gf1_stop_voice(gus, voice->number); | ||
362 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
363 | snd_gf1_select_voice(gus, voice->number); | ||
364 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); | ||
365 | voice->venv_state = VENV_BEFORE; | ||
366 | voice->volume_control = 0x03; | ||
367 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); | ||
368 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); | ||
369 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); | ||
370 | if (!gus->gf1.enh_mode) { | ||
371 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); | ||
372 | } else { | ||
373 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo); | ||
374 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo); | ||
375 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro); | ||
376 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro); | ||
377 | snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator); | ||
378 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume); | ||
379 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume); | ||
380 | } | ||
381 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
382 | do_volume_envelope(gus, voice); | ||
383 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
384 | snd_gf1_select_voice(gus, voice->number); | ||
385 | if (gus->gf1.enh_mode) | ||
386 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode); | ||
387 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control); | ||
388 | if (!gus->gf1.enh_mode) { | ||
389 | snd_gf1_delay(gus); | ||
390 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control ); | ||
391 | } | ||
392 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
393 | #if 0 | ||
394 | snd_gf1_print_voice_registers(gus); | ||
395 | #endif | ||
396 | voice->flags |= SNDRV_GF1_VFLG_RUNNING; | ||
397 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
398 | } | ||
399 | |||
400 | static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode) | ||
401 | { | ||
402 | unsigned char control; | ||
403 | unsigned long flags; | ||
404 | |||
405 | if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING)) | ||
406 | return; | ||
407 | switch (mode) { | ||
408 | default: | ||
409 | if (gus->gf1.volume_ramp > 0) { | ||
410 | if (voice->venv_state < VENV_RELEASE) { | ||
411 | voice->venv_state = VENV_RELEASE; | ||
412 | do_volume_envelope(gus, voice); | ||
413 | } | ||
414 | } | ||
415 | if (mode != SAMPLE_STOP_VENVELOPE) { | ||
416 | snd_gf1_stop_voice(gus, voice->number); | ||
417 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
418 | snd_gf1_select_voice(gus, voice->number); | ||
419 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); | ||
420 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
421 | voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
422 | } | ||
423 | break; | ||
424 | case SAMPLE_STOP_LOOP: /* disable loop only */ | ||
425 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
426 | snd_gf1_select_voice(gus, voice->number); | ||
427 | control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
428 | control &= ~(0x83 | 0x04); | ||
429 | control |= 0x20; | ||
430 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control); | ||
431 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
432 | break; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq) | ||
437 | { | ||
438 | unsigned long flags; | ||
439 | |||
440 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
441 | voice->fc_register = snd_gf1_translate_freq(gus, freq); | ||
442 | snd_gf1_select_voice(gus, voice->number); | ||
443 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); | ||
444 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
445 | } | ||
446 | |||
447 | static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume) | ||
448 | { | ||
449 | if (volume->volume >= 0) { | ||
450 | volume->volume &= 0x3fff; | ||
451 | voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4; | ||
452 | voice->venv_state_prev = VENV_SUSTAIN; | ||
453 | voice->venv_state = VENV_VOLUME; | ||
454 | do_volume_envelope(gus, voice); | ||
455 | } | ||
456 | if (volume->lr >= 0) { | ||
457 | volume->lr &= 0x3fff; | ||
458 | if (!gus->gf1.enh_mode) { | ||
459 | voice->gf1_pan = (volume->lr >> 10) & 15; | ||
460 | if (!gus->gf1.full_range_pan) { | ||
461 | if (voice->gf1_pan == 0) | ||
462 | voice->gf1_pan++; | ||
463 | if (voice->gf1_pan == 15) | ||
464 | voice->gf1_pan--; | ||
465 | } | ||
466 | voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */ | ||
467 | do_pan_envelope(gus, voice); | ||
468 | } else { | ||
469 | set_enhanced_pan(gus, voice, volume->lr >> 7); | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | |||
474 | static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop) | ||
475 | { | ||
476 | unsigned long flags; | ||
477 | int w_16 = voice->control & 0x04; | ||
478 | unsigned int begin, addr_start, addr_end; | ||
479 | simple_instrument_t *simple; | ||
480 | snd_seq_kinstr_t *instr; | ||
481 | |||
482 | #if 0 | ||
483 | printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); | ||
484 | #endif | ||
485 | instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); | ||
486 | if (instr == NULL) | ||
487 | return; | ||
488 | voice->instr = instr->instr; /* copy ID to speedup aliases */ | ||
489 | simple = KINSTR_DATA(instr); | ||
490 | begin = simple->address.memory; | ||
491 | addr_start = loop->start; | ||
492 | addr_end = loop->end; | ||
493 | addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin; | ||
494 | addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin; | ||
495 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
496 | snd_gf1_select_voice(gus, voice->number); | ||
497 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); | ||
498 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); | ||
499 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
500 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
501 | } | ||
502 | |||
503 | static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) | ||
504 | { | ||
505 | unsigned long flags; | ||
506 | int w_16 = voice->control & 0x04; | ||
507 | unsigned int begin, addr; | ||
508 | simple_instrument_t *simple; | ||
509 | snd_seq_kinstr_t *instr; | ||
510 | |||
511 | #if 0 | ||
512 | printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); | ||
513 | #endif | ||
514 | instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); | ||
515 | if (instr == NULL) | ||
516 | return; | ||
517 | voice->instr = instr->instr; /* copy ID to speedup aliases */ | ||
518 | simple = KINSTR_DATA(instr); | ||
519 | begin = simple->address.memory; | ||
520 | addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin; | ||
521 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
522 | snd_gf1_select_voice(gus, voice->number); | ||
523 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); | ||
524 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
525 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
526 | } | ||
527 | |||
528 | #if 0 | ||
529 | |||
530 | static unsigned char get_effects_mask( ultra_card_t *card, int value ) | ||
531 | { | ||
532 | if ( value > 7 ) return 0; | ||
533 | if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE ) | ||
534 | return card -> gf1.effects -> chip.interwave.voice_output[ value ]; | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | #endif | ||
539 | |||
540 | static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data) | ||
541 | { | ||
542 | #if 0 | ||
543 | unsigned long flags; | ||
544 | unsigned char uc; | ||
545 | |||
546 | switch ( *data ) { | ||
547 | case ULTRA_PRIV1_IW_EFFECT: | ||
548 | uc = get_effects_mask( card, ultra_get_byte( data, 4 ) ); | ||
549 | uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 ); | ||
550 | uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) ); | ||
551 | uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 ); | ||
552 | voice -> data.simple.effect_accumulator = uc; | ||
553 | voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4; | ||
554 | if ( !card -> gf1.enh_mode ) return; | ||
555 | if ( voice -> flags & VFLG_WAIT_FOR_START ) return; | ||
556 | if ( voice -> flags & VFLG_RUNNING ) | ||
557 | { | ||
558 | CLI( &flags ); | ||
559 | gf1_select_voice( card, voice -> number ); | ||
560 | ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator ); | ||
561 | ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume ); | ||
562 | STI( &flags ); | ||
563 | } | ||
564 | break; | ||
565 | case ULTRA_PRIV1_IW_LFO: | ||
566 | ultra_lfo_command( card, voice -> number, data ); | ||
567 | } | ||
568 | #endif | ||
569 | } | ||
570 | |||
571 | #if 0 | ||
572 | |||
573 | /* | ||
574 | * | ||
575 | */ | ||
576 | |||
577 | static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait ) | ||
578 | { | ||
579 | } | ||
580 | |||
581 | static void note_wait( ultra_card_t *card, ultra_voice_t *voice ) | ||
582 | { | ||
583 | } | ||
584 | |||
585 | static void note_off( ultra_card_t *card, ultra_voice_t *voice ) | ||
586 | { | ||
587 | } | ||
588 | |||
589 | static void note_volume( ultra_card_t *card, ultra_voice_t *voice ) | ||
590 | { | ||
591 | } | ||
592 | |||
593 | static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice ) | ||
594 | { | ||
595 | } | ||
596 | |||
597 | static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice ) | ||
598 | { | ||
599 | } | ||
600 | |||
601 | static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice ) | ||
602 | { | ||
603 | } | ||
604 | |||
605 | /* | ||
606 | * | ||
607 | */ | ||
608 | |||
609 | static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ) | ||
610 | { | ||
611 | } | ||
612 | |||
613 | static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ) | ||
614 | { | ||
615 | } | ||
616 | |||
617 | static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ) | ||
618 | { | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | * | ||
623 | */ | ||
624 | |||
625 | #endif | ||
626 | |||
627 | void snd_gf1_simple_init(snd_gus_voice_t *voice) | ||
628 | { | ||
629 | voice->handler_wave = interrupt_wave; | ||
630 | voice->handler_volume = interrupt_volume; | ||
631 | voice->handler_effect = interrupt_effect; | ||
632 | voice->volume_change = NULL; | ||
633 | voice->sample_ops = &sample_ops; | ||
634 | } | ||
diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c new file mode 100644 index 000000000000..66552e6013a4 --- /dev/null +++ b/sound/isa/gus/gus_synth.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Synthesizer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | #include <sound/seq_device.h> | ||
28 | |||
29 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
30 | MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer"); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | /* | ||
34 | * | ||
35 | */ | ||
36 | |||
37 | static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port) | ||
38 | { | ||
39 | int idx; | ||
40 | snd_gus_voice_t * voice; | ||
41 | |||
42 | for (idx = 0; idx < 32; idx++) { | ||
43 | voice = &gus->gf1.voices[idx]; | ||
44 | if (voice->use && voice->client == client && voice->port == port) | ||
45 | snd_gf1_free_voice(gus, voice); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info) | ||
50 | { | ||
51 | snd_gus_port_t * port = (snd_gus_port_t *)private_data; | ||
52 | snd_gus_card_t * gus = port->gus; | ||
53 | snd_gus_voice_t * voice; | ||
54 | unsigned int idx; | ||
55 | |||
56 | if (info->voices > 32) | ||
57 | return -EINVAL; | ||
58 | down(&gus->register_mutex); | ||
59 | if (!snd_gus_use_inc(gus)) { | ||
60 | up(&gus->register_mutex); | ||
61 | return -EFAULT; | ||
62 | } | ||
63 | for (idx = 0; idx < info->voices; idx++) { | ||
64 | voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); | ||
65 | if (voice == NULL) { | ||
66 | snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); | ||
67 | snd_gus_use_dec(gus); | ||
68 | up(&gus->register_mutex); | ||
69 | return -EBUSY; | ||
70 | } | ||
71 | voice->index = idx; | ||
72 | } | ||
73 | up(&gus->register_mutex); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info) | ||
78 | { | ||
79 | snd_gus_port_t * port = (snd_gus_port_t *)private_data; | ||
80 | snd_gus_card_t * gus = port->gus; | ||
81 | |||
82 | down(&gus->register_mutex); | ||
83 | snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); | ||
84 | snd_gus_use_dec(gus); | ||
85 | up(&gus->register_mutex); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * | ||
91 | */ | ||
92 | |||
93 | static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client) | ||
94 | { | ||
95 | snd_seq_instr_header_t ifree; | ||
96 | |||
97 | memset(&ifree, 0, sizeof(ifree)); | ||
98 | ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; | ||
99 | snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0); | ||
100 | } | ||
101 | |||
102 | int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) | ||
103 | { | ||
104 | snd_gus_port_t * p = (snd_gus_port_t *) private_data; | ||
105 | |||
106 | snd_assert(p != NULL, return -EINVAL); | ||
107 | if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && | ||
108 | ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { | ||
109 | snd_gus_sample_event(ev, p); | ||
110 | return 0; | ||
111 | } | ||
112 | if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && | ||
113 | ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { | ||
114 | if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { | ||
115 | snd_gus_synth_free_private_instruments(p, ev->data.addr.client); | ||
116 | return 0; | ||
117 | } | ||
118 | } | ||
119 | if (direct) { | ||
120 | if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { | ||
121 | snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops, | ||
122 | p->gus->gf1.ilist, | ||
123 | ev, | ||
124 | p->gus->gf1.seq_client, | ||
125 | atomic, hop); | ||
126 | return 0; | ||
127 | } | ||
128 | } | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static void snd_gus_synth_instr_notify(void *private_data, | ||
133 | snd_seq_kinstr_t *instr, | ||
134 | int what) | ||
135 | { | ||
136 | unsigned int idx; | ||
137 | snd_gus_card_t *gus = private_data; | ||
138 | snd_gus_voice_t *pvoice; | ||
139 | unsigned long flags; | ||
140 | |||
141 | spin_lock_irqsave(&gus->event_lock, flags); | ||
142 | for (idx = 0; idx < 32; idx++) { | ||
143 | pvoice = &gus->gf1.voices[idx]; | ||
144 | if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { | ||
145 | if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { | ||
146 | pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY); | ||
147 | } else { | ||
148 | snd_gf1_stop_voice(gus, pvoice->number); | ||
149 | pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | spin_unlock_irqrestore(&gus->event_lock, flags); | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * | ||
158 | */ | ||
159 | |||
160 | static void snd_gus_synth_free_port(void *private_data) | ||
161 | { | ||
162 | snd_gus_port_t * p = (snd_gus_port_t *)private_data; | ||
163 | |||
164 | if (p) | ||
165 | snd_midi_channel_free_set(p->chset); | ||
166 | } | ||
167 | |||
168 | static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx) | ||
169 | { | ||
170 | snd_gus_port_t * p; | ||
171 | snd_seq_port_callback_t callbacks; | ||
172 | char name[32]; | ||
173 | int result; | ||
174 | |||
175 | p = &gus->gf1.seq_ports[idx]; | ||
176 | p->chset = snd_midi_channel_alloc_set(16); | ||
177 | if (p->chset == NULL) | ||
178 | return -ENOMEM; | ||
179 | p->chset->private_data = p; | ||
180 | p->gus = gus; | ||
181 | p->client = gus->gf1.seq_client; | ||
182 | |||
183 | memset(&callbacks, 0, sizeof(callbacks)); | ||
184 | callbacks.owner = THIS_MODULE; | ||
185 | callbacks.use = snd_gus_synth_use; | ||
186 | callbacks.unuse = snd_gus_synth_unuse; | ||
187 | callbacks.event_input = snd_gus_synth_event_input; | ||
188 | callbacks.private_free = snd_gus_synth_free_port; | ||
189 | callbacks.private_data = p; | ||
190 | |||
191 | sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx); | ||
192 | p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client, | ||
193 | &callbacks, | ||
194 | SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, | ||
195 | SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | | ||
196 | SNDRV_SEQ_PORT_TYPE_SYNTH, | ||
197 | 16, 0, | ||
198 | name); | ||
199 | if (p->chset->port < 0) { | ||
200 | result = p->chset->port; | ||
201 | snd_gus_synth_free_port(p); | ||
202 | return result; | ||
203 | } | ||
204 | p->port = p->chset->port; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * | ||
210 | */ | ||
211 | |||
212 | static int snd_gus_synth_new_device(snd_seq_device_t *dev) | ||
213 | { | ||
214 | snd_gus_card_t *gus; | ||
215 | int client, i; | ||
216 | snd_seq_client_callback_t callbacks; | ||
217 | snd_seq_client_info_t *cinfo; | ||
218 | snd_seq_port_subscribe_t sub; | ||
219 | snd_iwffff_ops_t *iwops; | ||
220 | snd_gf1_ops_t *gf1ops; | ||
221 | snd_simple_ops_t *simpleops; | ||
222 | |||
223 | gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
224 | if (gus == NULL) | ||
225 | return -EINVAL; | ||
226 | |||
227 | init_MUTEX(&gus->register_mutex); | ||
228 | gus->gf1.seq_client = -1; | ||
229 | |||
230 | cinfo = kmalloc(sizeof(*cinfo), GFP_KERNEL); | ||
231 | if (! cinfo) | ||
232 | return -ENOMEM; | ||
233 | |||
234 | /* allocate new client */ | ||
235 | memset(&callbacks, 0, sizeof(callbacks)); | ||
236 | callbacks.private_data = gus; | ||
237 | callbacks.allow_output = callbacks.allow_input = 1; | ||
238 | client = gus->gf1.seq_client = | ||
239 | snd_seq_create_kernel_client(gus->card, 1, &callbacks); | ||
240 | if (client < 0) { | ||
241 | kfree(cinfo); | ||
242 | return client; | ||
243 | } | ||
244 | |||
245 | /* change name of client */ | ||
246 | memset(cinfo, 0, sizeof(*cinfo)); | ||
247 | cinfo->client = client; | ||
248 | cinfo->type = KERNEL_CLIENT; | ||
249 | sprintf(cinfo->name, gus->interwave ? "AMD InterWave" : "GF1"); | ||
250 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, cinfo); | ||
251 | kfree(cinfo); | ||
252 | |||
253 | for (i = 0; i < 4; i++) | ||
254 | snd_gus_synth_create_port(gus, i); | ||
255 | |||
256 | gus->gf1.ilist = snd_seq_instr_list_new(); | ||
257 | if (gus->gf1.ilist == NULL) { | ||
258 | snd_seq_delete_kernel_client(client); | ||
259 | gus->gf1.seq_client = -1; | ||
260 | return -ENOMEM; | ||
261 | } | ||
262 | gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; | ||
263 | |||
264 | simpleops = &gus->gf1.simple_ops; | ||
265 | snd_seq_simple_init(simpleops, gus, NULL); | ||
266 | simpleops->put_sample = snd_gus_simple_put_sample; | ||
267 | simpleops->get_sample = snd_gus_simple_get_sample; | ||
268 | simpleops->remove_sample = snd_gus_simple_remove_sample; | ||
269 | simpleops->notify = snd_gus_synth_instr_notify; | ||
270 | |||
271 | gf1ops = &gus->gf1.gf1_ops; | ||
272 | snd_seq_gf1_init(gf1ops, gus, &simpleops->kops); | ||
273 | gf1ops->put_sample = snd_gus_gf1_put_sample; | ||
274 | gf1ops->get_sample = snd_gus_gf1_get_sample; | ||
275 | gf1ops->remove_sample = snd_gus_gf1_remove_sample; | ||
276 | gf1ops->notify = snd_gus_synth_instr_notify; | ||
277 | |||
278 | iwops = &gus->gf1.iwffff_ops; | ||
279 | snd_seq_iwffff_init(iwops, gus, &gf1ops->kops); | ||
280 | iwops->put_sample = snd_gus_iwffff_put_sample; | ||
281 | iwops->get_sample = snd_gus_iwffff_get_sample; | ||
282 | iwops->remove_sample = snd_gus_iwffff_remove_sample; | ||
283 | iwops->notify = snd_gus_synth_instr_notify; | ||
284 | |||
285 | memset(&sub, 0, sizeof(sub)); | ||
286 | sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; | ||
287 | sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; | ||
288 | sub.dest.client = client; | ||
289 | sub.dest.port = 0; | ||
290 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int snd_gus_synth_delete_device(snd_seq_device_t *dev) | ||
296 | { | ||
297 | snd_gus_card_t *gus; | ||
298 | |||
299 | gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
300 | if (gus == NULL) | ||
301 | return -EINVAL; | ||
302 | |||
303 | if (gus->gf1.seq_client >= 0) { | ||
304 | snd_seq_delete_kernel_client(gus->gf1.seq_client); | ||
305 | gus->gf1.seq_client = -1; | ||
306 | } | ||
307 | if (gus->gf1.ilist) | ||
308 | snd_seq_instr_list_free(&gus->gf1.ilist); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int __init alsa_gus_synth_init(void) | ||
313 | { | ||
314 | static snd_seq_dev_ops_t ops = { | ||
315 | snd_gus_synth_new_device, | ||
316 | snd_gus_synth_delete_device | ||
317 | }; | ||
318 | |||
319 | return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops, | ||
320 | sizeof(snd_gus_card_t*)); | ||
321 | } | ||
322 | |||
323 | static void __exit alsa_gus_synth_exit(void) | ||
324 | { | ||
325 | snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS); | ||
326 | } | ||
327 | |||
328 | module_init(alsa_gus_synth_init) | ||
329 | module_exit(alsa_gus_synth_exit) | ||
diff --git a/sound/isa/gus/gus_tables.h b/sound/isa/gus/gus_tables.h new file mode 100644 index 000000000000..ed8e9d85ad31 --- /dev/null +++ b/sound/isa/gus/gus_tables.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #define SNDRV_GF1_SCALE_TABLE_SIZE 128 | ||
22 | #define SNDRV_GF1_ATTEN_TABLE_SIZE 128 | ||
23 | |||
24 | #ifdef __GUS_TABLES_ALLOC__ | ||
25 | |||
26 | unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] = | ||
27 | { | ||
28 | 8372, 8870, 9397, 9956, 10548, 11175, | ||
29 | 11840, 12544, 13290, 14080, 14917, 15804, | ||
30 | 16744, 17740, 18795, 19912, 21096, 22351, | ||
31 | 23680, 25088, 26580, 28160, 29834, 31609, | ||
32 | 33488, 35479, 37589, 39824, 42192, 44701, | ||
33 | 47359, 50175, 53159, 56320, 59669, 63217, | ||
34 | 66976, 70959, 75178, 79649, 84385, 89402, | ||
35 | 94719, 100351, 106318, 112640, 119338, 126434, | ||
36 | 133952, 141918, 150356, 159297, 168769, 178805, | ||
37 | 189437, 200702, 212636, 225280, 238676, 252868, | ||
38 | 267905, 283835, 300713, 318594, 337539, 357610, | ||
39 | 378874, 401403, 425272, 450560, 477352, 505737, | ||
40 | 535809, 567670, 601425, 637188, 675077, 715219, | ||
41 | 757749, 802807, 850544, 901120, 954703, 1011473, | ||
42 | 1071618, 1135340, 1202851, 1274376, 1350154, 1430439, | ||
43 | 1515497, 1605613, 1701088, 1802240, 1909407, 2022946, | ||
44 | 2143237, 2270680, 2405702, 2548752, 2700309, 2860878, | ||
45 | 3030994, 3211227, 3402176, 3604480, 3818814, 4045892, | ||
46 | 4286473, 4541360, 4811404, 5097505, 5400618, 5721755, | ||
47 | 6061989, 6422453, 6804352, 7208960, 7637627, 8091784, | ||
48 | 8572947, 9082720, 9622807, 10195009, 10801236, 11443511, | ||
49 | 12123977, 12844906 | ||
50 | }; | ||
51 | |||
52 | unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = { | ||
53 | 4095 /* 0 */,1789 /* 1 */,1533 /* 2 */,1383 /* 3 */,1277 /* 4 */, | ||
54 | 1195 /* 5 */,1127 /* 6 */,1070 /* 7 */,1021 /* 8 */,978 /* 9 */, | ||
55 | 939 /* 10 */,903 /* 11 */,871 /* 12 */,842 /* 13 */,814 /* 14 */, | ||
56 | 789 /* 15 */,765 /* 16 */,743 /* 17 */,722 /* 18 */,702 /* 19 */, | ||
57 | 683 /* 20 */,665 /* 21 */,647 /* 22 */,631 /* 23 */,615 /* 24 */, | ||
58 | 600 /* 25 */,586 /* 26 */,572 /* 27 */,558 /* 28 */,545 /* 29 */, | ||
59 | 533 /* 30 */,521 /* 31 */,509 /* 32 */,498 /* 33 */,487 /* 34 */, | ||
60 | 476 /* 35 */,466 /* 36 */,455 /* 37 */,446 /* 38 */,436 /* 39 */, | ||
61 | 427 /* 40 */,418 /* 41 */,409 /* 42 */,400 /* 43 */,391 /* 44 */, | ||
62 | 383 /* 45 */,375 /* 46 */,367 /* 47 */,359 /* 48 */,352 /* 49 */, | ||
63 | 344 /* 50 */,337 /* 51 */,330 /* 52 */,323 /* 53 */,316 /* 54 */, | ||
64 | 309 /* 55 */,302 /* 56 */,296 /* 57 */,289 /* 58 */,283 /* 59 */, | ||
65 | 277 /* 60 */,271 /* 61 */,265 /* 62 */,259 /* 63 */,253 /* 64 */, | ||
66 | 247 /* 65 */,242 /* 66 */,236 /* 67 */,231 /* 68 */,225 /* 69 */, | ||
67 | 220 /* 70 */,215 /* 71 */,210 /* 72 */,205 /* 73 */,199 /* 74 */, | ||
68 | 195 /* 75 */,190 /* 76 */,185 /* 77 */,180 /* 78 */,175 /* 79 */, | ||
69 | 171 /* 80 */,166 /* 81 */,162 /* 82 */,157 /* 83 */,153 /* 84 */, | ||
70 | 148 /* 85 */,144 /* 86 */,140 /* 87 */,135 /* 88 */,131 /* 89 */, | ||
71 | 127 /* 90 */,123 /* 91 */,119 /* 92 */,115 /* 93 */,111 /* 94 */, | ||
72 | 107 /* 95 */,103 /* 96 */,100 /* 97 */,96 /* 98 */,92 /* 99 */, | ||
73 | 88 /* 100 */,85 /* 101 */,81 /* 102 */,77 /* 103 */,74 /* 104 */, | ||
74 | 70 /* 105 */,67 /* 106 */,63 /* 107 */,60 /* 108 */,56 /* 109 */, | ||
75 | 53 /* 110 */,50 /* 111 */,46 /* 112 */,43 /* 113 */,40 /* 114 */, | ||
76 | 37 /* 115 */,33 /* 116 */,30 /* 117 */,27 /* 118 */,24 /* 119 */, | ||
77 | 21 /* 120 */,18 /* 121 */,15 /* 122 */,12 /* 123 */,9 /* 124 */, | ||
78 | 6 /* 125 */,3 /* 126 */,0 /* 127 */, | ||
79 | }; | ||
80 | |||
81 | #else | ||
82 | |||
83 | extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE]; | ||
84 | extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE]; | ||
85 | |||
86 | #endif | ||
diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c new file mode 100644 index 000000000000..9876603ff6c1 --- /dev/null +++ b/sound/isa/gus/gus_timer.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Timers | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * GUS have similar timers as AdLib (OPL2/OPL3 chips). | ||
6 | * | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/gus.h> | ||
28 | |||
29 | /* | ||
30 | * Timer 1 - 80us | ||
31 | */ | ||
32 | |||
33 | static int snd_gf1_timer1_start(snd_timer_t * timer) | ||
34 | { | ||
35 | unsigned long flags; | ||
36 | unsigned char tmp; | ||
37 | unsigned int ticks; | ||
38 | snd_gus_card_t *gus; | ||
39 | |||
40 | gus = snd_timer_chip(timer); | ||
41 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
42 | ticks = timer->sticks; | ||
43 | tmp = (gus->gf1.timer_enabled |= 4); | ||
44 | snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */ | ||
45 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */ | ||
46 | snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ | ||
47 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int snd_gf1_timer1_stop(snd_timer_t * timer) | ||
52 | { | ||
53 | unsigned long flags; | ||
54 | unsigned char tmp; | ||
55 | snd_gus_card_t *gus; | ||
56 | |||
57 | gus = snd_timer_chip(timer); | ||
58 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
59 | tmp = (gus->gf1.timer_enabled &= ~4); | ||
60 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ | ||
61 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * Timer 2 - 320us | ||
67 | */ | ||
68 | |||
69 | static int snd_gf1_timer2_start(snd_timer_t * timer) | ||
70 | { | ||
71 | unsigned long flags; | ||
72 | unsigned char tmp; | ||
73 | unsigned int ticks; | ||
74 | snd_gus_card_t *gus; | ||
75 | |||
76 | gus = snd_timer_chip(timer); | ||
77 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
78 | ticks = timer->sticks; | ||
79 | tmp = (gus->gf1.timer_enabled |= 8); | ||
80 | snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */ | ||
81 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */ | ||
82 | snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ | ||
83 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int snd_gf1_timer2_stop(snd_timer_t * timer) | ||
88 | { | ||
89 | unsigned long flags; | ||
90 | unsigned char tmp; | ||
91 | snd_gus_card_t *gus; | ||
92 | |||
93 | gus = snd_timer_chip(timer); | ||
94 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
95 | tmp = (gus->gf1.timer_enabled &= ~8); | ||
96 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ | ||
97 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | |||
103 | */ | ||
104 | |||
105 | static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus) | ||
106 | { | ||
107 | snd_timer_t *timer = gus->gf1.timer1; | ||
108 | |||
109 | if (timer == NULL) | ||
110 | return; | ||
111 | snd_timer_interrupt(timer, timer->sticks); | ||
112 | } | ||
113 | |||
114 | static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus) | ||
115 | { | ||
116 | snd_timer_t *timer = gus->gf1.timer2; | ||
117 | |||
118 | if (timer == NULL) | ||
119 | return; | ||
120 | snd_timer_interrupt(timer, timer->sticks); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | |||
125 | */ | ||
126 | |||
127 | static struct _snd_timer_hardware snd_gf1_timer1 = | ||
128 | { | ||
129 | .flags = SNDRV_TIMER_HW_STOP, | ||
130 | .resolution = 80000, | ||
131 | .ticks = 256, | ||
132 | .start = snd_gf1_timer1_start, | ||
133 | .stop = snd_gf1_timer1_stop, | ||
134 | }; | ||
135 | |||
136 | static struct _snd_timer_hardware snd_gf1_timer2 = | ||
137 | { | ||
138 | .flags = SNDRV_TIMER_HW_STOP, | ||
139 | .resolution = 320000, | ||
140 | .ticks = 256, | ||
141 | .start = snd_gf1_timer2_start, | ||
142 | .stop = snd_gf1_timer2_stop, | ||
143 | }; | ||
144 | |||
145 | static void snd_gf1_timer1_free(snd_timer_t *timer) | ||
146 | { | ||
147 | snd_gus_card_t *gus = timer->private_data; | ||
148 | gus->gf1.timer1 = NULL; | ||
149 | } | ||
150 | |||
151 | static void snd_gf1_timer2_free(snd_timer_t *timer) | ||
152 | { | ||
153 | snd_gus_card_t *gus = timer->private_data; | ||
154 | gus->gf1.timer2 = NULL; | ||
155 | } | ||
156 | |||
157 | void snd_gf1_timers_init(snd_gus_card_t * gus) | ||
158 | { | ||
159 | snd_timer_t *timer; | ||
160 | snd_timer_id_t tid; | ||
161 | |||
162 | if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL) | ||
163 | return; | ||
164 | |||
165 | gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; | ||
166 | gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; | ||
167 | |||
168 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
169 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
170 | tid.card = gus->card->number; | ||
171 | tid.device = gus->timer_dev; | ||
172 | tid.subdevice = 0; | ||
173 | |||
174 | if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { | ||
175 | strcpy(timer->name, "GF1 timer #1"); | ||
176 | timer->private_data = gus; | ||
177 | timer->private_free = snd_gf1_timer1_free; | ||
178 | timer->hw = snd_gf1_timer1; | ||
179 | } | ||
180 | gus->gf1.timer1 = timer; | ||
181 | |||
182 | tid.device++; | ||
183 | |||
184 | if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { | ||
185 | strcpy(timer->name, "GF1 timer #2"); | ||
186 | timer->private_data = gus; | ||
187 | timer->private_free = snd_gf1_timer2_free; | ||
188 | timer->hw = snd_gf1_timer2; | ||
189 | } | ||
190 | gus->gf1.timer2 = timer; | ||
191 | } | ||
192 | |||
193 | void snd_gf1_timers_done(snd_gus_card_t * gus) | ||
194 | { | ||
195 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2); | ||
196 | if (gus->gf1.timer1) { | ||
197 | snd_device_free(gus->card, gus->gf1.timer1); | ||
198 | gus->gf1.timer1 = NULL; | ||
199 | } | ||
200 | if (gus->gf1.timer2) { | ||
201 | snd_device_free(gus->card, gus->gf1.timer2); | ||
202 | gus->gf1.timer2 = NULL; | ||
203 | } | ||
204 | } | ||
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c new file mode 100644 index 000000000000..1bc2da8784e0 --- /dev/null +++ b/sound/isa/gus/gus_uart.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for the GF1 MIDI interface - like UART 6850 | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/gus.h> | ||
28 | |||
29 | static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) | ||
30 | { | ||
31 | int count; | ||
32 | unsigned char stat, data, byte; | ||
33 | unsigned long flags; | ||
34 | |||
35 | count = 10; | ||
36 | while (count) { | ||
37 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
38 | stat = snd_gf1_uart_stat(gus); | ||
39 | if (!(stat & 0x01)) { /* data in Rx FIFO? */ | ||
40 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
41 | count--; | ||
42 | continue; | ||
43 | } | ||
44 | count = 100; /* arm counter to new value */ | ||
45 | data = snd_gf1_uart_get(gus); | ||
46 | if (!(gus->gf1.uart_cmd & 0x80)) { | ||
47 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
48 | continue; | ||
49 | } | ||
50 | if (stat & 0x10) { /* framing error */ | ||
51 | gus->gf1.uart_framing++; | ||
52 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
53 | continue; | ||
54 | } | ||
55 | byte = snd_gf1_uart_get(gus); | ||
56 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
57 | snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); | ||
58 | if (stat & 0x20) { | ||
59 | gus->gf1.uart_overrun++; | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus) | ||
65 | { | ||
66 | char byte; | ||
67 | unsigned long flags; | ||
68 | |||
69 | /* try unlock output */ | ||
70 | if (snd_gf1_uart_stat(gus) & 0x01) | ||
71 | snd_gf1_interrupt_midi_in(gus); | ||
72 | |||
73 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
74 | if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ | ||
75 | if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ | ||
76 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ | ||
77 | } else { | ||
78 | snd_gf1_uart_put(gus, byte); | ||
79 | } | ||
80 | } | ||
81 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
82 | } | ||
83 | |||
84 | static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close) | ||
85 | { | ||
86 | snd_gf1_uart_cmd(gus, 0x03); /* reset */ | ||
87 | if (!close && gus->uart_enable) { | ||
88 | udelay(160); | ||
89 | snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ | ||
90 | } | ||
91 | } | ||
92 | |||
93 | static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream) | ||
94 | { | ||
95 | unsigned long flags; | ||
96 | snd_gus_card_t *gus; | ||
97 | |||
98 | gus = substream->rmidi->private_data; | ||
99 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
100 | if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ | ||
101 | snd_gf1_uart_reset(gus, 0); | ||
102 | } | ||
103 | gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; | ||
104 | gus->midi_substream_output = substream; | ||
105 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
106 | #if 0 | ||
107 | snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); | ||
108 | #endif | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream) | ||
113 | { | ||
114 | unsigned long flags; | ||
115 | snd_gus_card_t *gus; | ||
116 | int i; | ||
117 | |||
118 | gus = substream->rmidi->private_data; | ||
119 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
120 | if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { | ||
121 | snd_gf1_uart_reset(gus, 0); | ||
122 | } | ||
123 | gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; | ||
124 | gus->midi_substream_input = substream; | ||
125 | if (gus->uart_enable) { | ||
126 | for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) | ||
127 | snd_gf1_uart_get(gus); /* clean Rx */ | ||
128 | if (i >= 1000) | ||
129 | snd_printk("gus midi uart init read - cleanup error\n"); | ||
130 | } | ||
131 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
132 | #if 0 | ||
133 | snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); | ||
134 | snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); | ||
135 | #endif | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream) | ||
140 | { | ||
141 | unsigned long flags; | ||
142 | snd_gus_card_t *gus; | ||
143 | |||
144 | gus = substream->rmidi->private_data; | ||
145 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
146 | if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) | ||
147 | snd_gf1_uart_reset(gus, 1); | ||
148 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); | ||
149 | gus->midi_substream_output = NULL; | ||
150 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream) | ||
155 | { | ||
156 | unsigned long flags; | ||
157 | snd_gus_card_t *gus; | ||
158 | |||
159 | gus = substream->rmidi->private_data; | ||
160 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
161 | if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) | ||
162 | snd_gf1_uart_reset(gus, 1); | ||
163 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); | ||
164 | gus->midi_substream_input = NULL; | ||
165 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
170 | { | ||
171 | snd_gus_card_t *gus; | ||
172 | unsigned long flags; | ||
173 | |||
174 | gus = substream->rmidi->private_data; | ||
175 | |||
176 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
177 | if (up) { | ||
178 | if ((gus->gf1.uart_cmd & 0x80) == 0) | ||
179 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ | ||
180 | } else { | ||
181 | if (gus->gf1.uart_cmd & 0x80) | ||
182 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ | ||
183 | } | ||
184 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
185 | } | ||
186 | |||
187 | static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
188 | { | ||
189 | unsigned long flags; | ||
190 | snd_gus_card_t *gus; | ||
191 | char byte; | ||
192 | int timeout; | ||
193 | |||
194 | gus = substream->rmidi->private_data; | ||
195 | |||
196 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
197 | if (up) { | ||
198 | if ((gus->gf1.uart_cmd & 0x20) == 0) { | ||
199 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
200 | /* wait for empty Rx - Tx is probably unlocked */ | ||
201 | timeout = 10000; | ||
202 | while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); | ||
203 | /* Tx FIFO free? */ | ||
204 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
205 | if (gus->gf1.uart_cmd & 0x20) { | ||
206 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
207 | return; | ||
208 | } | ||
209 | if (snd_gf1_uart_stat(gus) & 0x02) { | ||
210 | if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { | ||
211 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
212 | return; | ||
213 | } | ||
214 | snd_gf1_uart_put(gus, byte); | ||
215 | } | ||
216 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ | ||
217 | } | ||
218 | } else { | ||
219 | if (gus->gf1.uart_cmd & 0x20) | ||
220 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); | ||
221 | } | ||
222 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
223 | } | ||
224 | |||
225 | static snd_rawmidi_ops_t snd_gf1_uart_output = | ||
226 | { | ||
227 | .open = snd_gf1_uart_output_open, | ||
228 | .close = snd_gf1_uart_output_close, | ||
229 | .trigger = snd_gf1_uart_output_trigger, | ||
230 | }; | ||
231 | |||
232 | static snd_rawmidi_ops_t snd_gf1_uart_input = | ||
233 | { | ||
234 | .open = snd_gf1_uart_input_open, | ||
235 | .close = snd_gf1_uart_input_close, | ||
236 | .trigger = snd_gf1_uart_input_trigger, | ||
237 | }; | ||
238 | |||
239 | int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi) | ||
240 | { | ||
241 | snd_rawmidi_t *rmidi; | ||
242 | int err; | ||
243 | |||
244 | if (rrawmidi) | ||
245 | *rrawmidi = NULL; | ||
246 | if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) | ||
247 | return err; | ||
248 | strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); | ||
249 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); | ||
250 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); | ||
251 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
252 | rmidi->private_data = gus; | ||
253 | gus->midi_uart = rmidi; | ||
254 | if (rrawmidi) | ||
255 | *rrawmidi = rmidi; | ||
256 | return err; | ||
257 | } | ||
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c new file mode 100644 index 000000000000..b72bcfb28617 --- /dev/null +++ b/sound/isa/gus/gus_volume.c | |||
@@ -0,0 +1,210 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/time.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/gus.h> | ||
25 | #define __GUS_TABLES_ALLOC__ | ||
26 | #include "gus_tables.h" | ||
27 | |||
28 | EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */ | ||
29 | |||
30 | unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol) | ||
31 | { | ||
32 | unsigned short e, m, tmp; | ||
33 | |||
34 | if (vol > 65535) | ||
35 | vol = 65535; | ||
36 | tmp = vol; | ||
37 | e = 7; | ||
38 | if (tmp < 128) { | ||
39 | while (e > 0 && tmp < (1 << e)) | ||
40 | e--; | ||
41 | } else { | ||
42 | while (tmp > 255) { | ||
43 | tmp >>= 1; | ||
44 | e++; | ||
45 | } | ||
46 | } | ||
47 | m = vol - (1 << e); | ||
48 | if (m > 0) { | ||
49 | if (e > 8) | ||
50 | m >>= e - 8; | ||
51 | else if (e < 8) | ||
52 | m <<= 8 - e; | ||
53 | m &= 255; | ||
54 | } | ||
55 | return (e << 8) | m; | ||
56 | } | ||
57 | |||
58 | unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol) | ||
59 | { | ||
60 | unsigned int rvol; | ||
61 | unsigned short e, m; | ||
62 | |||
63 | if (!gf1_vol) | ||
64 | return 0; | ||
65 | e = gf1_vol >> 8; | ||
66 | m = (unsigned char) gf1_vol; | ||
67 | rvol = 1 << e; | ||
68 | if (e > 8) | ||
69 | return rvol | (m << (e - 8)); | ||
70 | return rvol | (m >> (8 - e)); | ||
71 | } | ||
72 | |||
73 | unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, | ||
74 | unsigned short start, | ||
75 | unsigned short end, | ||
76 | unsigned int us) | ||
77 | { | ||
78 | static unsigned char vol_rates[19] = | ||
79 | { | ||
80 | 23, 24, 26, 28, 29, 31, 32, 34, | ||
81 | 36, 37, 39, 40, 42, 44, 45, 47, | ||
82 | 49, 50, 52 | ||
83 | }; | ||
84 | unsigned short range, increment, value, i; | ||
85 | |||
86 | start >>= 4; | ||
87 | end >>= 4; | ||
88 | if (start < end) | ||
89 | us /= end - start; | ||
90 | else | ||
91 | us /= start - end; | ||
92 | range = 4; | ||
93 | value = gus->gf1.enh_mode ? | ||
94 | vol_rates[0] : | ||
95 | vol_rates[gus->gf1.active_voices - 14]; | ||
96 | for (i = 0; i < 3; i++) { | ||
97 | if (us < value) { | ||
98 | range = i; | ||
99 | break; | ||
100 | } else | ||
101 | value <<= 3; | ||
102 | } | ||
103 | if (range == 4) { | ||
104 | range = 3; | ||
105 | increment = 1; | ||
106 | } else | ||
107 | increment = (value + (value >> 1)) / us; | ||
108 | return (range << 6) | (increment & 0x3f); | ||
109 | } | ||
110 | |||
111 | unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16) | ||
112 | { | ||
113 | freq16 >>= 3; | ||
114 | if (freq16 < 50) | ||
115 | freq16 = 50; | ||
116 | if (freq16 & 0xf8000000) { | ||
117 | freq16 = ~0xf8000000; | ||
118 | snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16); | ||
119 | } | ||
120 | return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq; | ||
121 | } | ||
122 | |||
123 | short snd_gf1_compute_vibrato(short cents, unsigned short fc_register) | ||
124 | { | ||
125 | static short vibrato_table[] = | ||
126 | { | ||
127 | 0, 0, 32, 592, 61, 1175, 93, 1808, | ||
128 | 124, 2433, 152, 3007, 182, 3632, 213, 4290, | ||
129 | 241, 4834, 255, 5200 | ||
130 | }; | ||
131 | |||
132 | long depth; | ||
133 | short *vi1, *vi2, pcents, v1; | ||
134 | |||
135 | pcents = cents < 0 ? -cents : cents; | ||
136 | for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2); | ||
137 | v1 = *(vi1 + 1); | ||
138 | /* The FC table above is a list of pairs. The first number in the pair */ | ||
139 | /* is the cents index from 0-255 cents, and the second number in the */ | ||
140 | /* pair is the FC adjustment needed to change the pitch by the indexed */ | ||
141 | /* number of cents. The table was created for an FC of 32768. */ | ||
142 | /* The following expression does a linear interpolation against the */ | ||
143 | /* approximated log curve in the table above, and then scales the number */ | ||
144 | /* by the FC before the LFO. This calculation also adjusts the output */ | ||
145 | /* value to produce the appropriate depth for the hardware. The depth */ | ||
146 | /* is 2 * desired FC + 1. */ | ||
147 | depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14; | ||
148 | if (depth) | ||
149 | depth++; | ||
150 | if (depth > 255) | ||
151 | depth = 255; | ||
152 | return cents < 0 ? -(short) depth : (short) depth; | ||
153 | } | ||
154 | |||
155 | unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens) | ||
156 | { | ||
157 | static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933}; | ||
158 | int wheel, sensitivity; | ||
159 | unsigned int mantissa, f1, f2; | ||
160 | unsigned short semitones, f1_index, f2_index, f1_power, f2_power; | ||
161 | char bend_down = 0; | ||
162 | int bend; | ||
163 | |||
164 | if (!sens) | ||
165 | return 1024; | ||
166 | wheel = (int) pitchbend - 8192; | ||
167 | sensitivity = ((int) sens * wheel) / 128; | ||
168 | if (sensitivity < 0) { | ||
169 | bend_down = 1; | ||
170 | sensitivity = -sensitivity; | ||
171 | } | ||
172 | semitones = (unsigned int) (sensitivity >> 13); | ||
173 | mantissa = sensitivity % 8192; | ||
174 | f1_index = semitones % 12; | ||
175 | f2_index = (semitones + 1) % 12; | ||
176 | f1_power = semitones / 12; | ||
177 | f2_power = (semitones + 1) / 12; | ||
178 | f1 = log_table[f1_index] << f1_power; | ||
179 | f2 = log_table[f2_index] << f2_power; | ||
180 | bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1); | ||
181 | if (bend_down) | ||
182 | bend = 1048576L / bend; | ||
183 | return bend; | ||
184 | } | ||
185 | |||
186 | unsigned short snd_gf1_compute_freq(unsigned int freq, | ||
187 | unsigned int rate, | ||
188 | unsigned short mix_rate) | ||
189 | { | ||
190 | unsigned int fc; | ||
191 | int scale = 0; | ||
192 | |||
193 | while (freq >= 4194304L) { | ||
194 | scale++; | ||
195 | freq >>= 1; | ||
196 | } | ||
197 | fc = (freq << 10) / rate; | ||
198 | if (fc > 97391L) { | ||
199 | fc = 97391; | ||
200 | snd_printk("patch: (1) fc frequency overflow - %u\n", fc); | ||
201 | } | ||
202 | fc = (fc * 44100UL) / mix_rate; | ||
203 | while (scale--) | ||
204 | fc <<= 1; | ||
205 | if (fc > 65535L) { | ||
206 | fc = 65535; | ||
207 | snd_printk("patch: (2) fc frequency overflow - %u\n", fc); | ||
208 | } | ||
209 | return (unsigned short) fc; | ||
210 | } | ||
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c new file mode 100644 index 000000000000..a99fa5040b46 --- /dev/null +++ b/sound/isa/gus/gusclassic.c | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Driver for Gravis UltraSound Classic soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #define SNDRV_LEGACY_AUTO_PROBE | ||
31 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
32 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
33 | #include <sound/initval.h> | ||
34 | |||
35 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
36 | MODULE_DESCRIPTION("Gravis UltraSound Classic"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Classic}}"); | ||
39 | |||
40 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
41 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
42 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
43 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ | ||
44 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */ | ||
45 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
46 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
47 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
48 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
49 | static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; | ||
50 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
51 | |||
52 | module_param_array(index, int, NULL, 0444); | ||
53 | MODULE_PARM_DESC(index, "Index value for GUS Classic soundcard."); | ||
54 | module_param_array(id, charp, NULL, 0444); | ||
55 | MODULE_PARM_DESC(id, "ID string for GUS Classic soundcard."); | ||
56 | module_param_array(enable, bool, NULL, 0444); | ||
57 | MODULE_PARM_DESC(enable, "Enable GUS Classic soundcard."); | ||
58 | module_param_array(port, long, NULL, 0444); | ||
59 | MODULE_PARM_DESC(port, "Port # for GUS Classic driver."); | ||
60 | module_param_array(irq, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(irq, "IRQ # for GUS Classic driver."); | ||
62 | module_param_array(dma1, int, NULL, 0444); | ||
63 | MODULE_PARM_DESC(dma1, "DMA1 # for GUS Classic driver."); | ||
64 | module_param_array(dma2, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(dma2, "DMA2 # for GUS Classic driver."); | ||
66 | module_param_array(joystick_dac, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver."); | ||
68 | module_param_array(channels, int, NULL, 0444); | ||
69 | MODULE_PARM_DESC(channels, "GF1 channels for GUS Classic driver."); | ||
70 | module_param_array(pcm_channels, int, NULL, 0444); | ||
71 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Classic driver."); | ||
72 | |||
73 | static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
74 | |||
75 | |||
76 | static int __init snd_gusclassic_detect(snd_gus_card_t * gus) | ||
77 | { | ||
78 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
79 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
80 | { | ||
81 | unsigned char d; | ||
82 | |||
83 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
84 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
85 | return -ENODEV; | ||
86 | } | ||
87 | } | ||
88 | #else | ||
89 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
90 | return -ENODEV; | ||
91 | #endif | ||
92 | udelay(160); | ||
93 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
94 | udelay(160); | ||
95 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
96 | { | ||
97 | unsigned char d; | ||
98 | |||
99 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
100 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
101 | return -ENODEV; | ||
102 | } | ||
103 | } | ||
104 | #else | ||
105 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
106 | return -ENODEV; | ||
107 | #endif | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus) | ||
113 | { | ||
114 | gus->equal_irq = 0; | ||
115 | gus->codec_flag = 0; | ||
116 | gus->max_flag = 0; | ||
117 | gus->joystick_dac = joystick_dac[dev]; | ||
118 | } | ||
119 | |||
120 | static int __init snd_gusclassic_probe(int dev) | ||
121 | { | ||
122 | static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1}; | ||
123 | static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; | ||
124 | int xirq, xdma1, xdma2; | ||
125 | snd_card_t *card; | ||
126 | struct snd_gusclassic *guscard; | ||
127 | snd_gus_card_t *gus = NULL; | ||
128 | int err; | ||
129 | |||
130 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
131 | if (card == NULL) | ||
132 | return -ENOMEM; | ||
133 | guscard = (struct snd_gusclassic *)card->private_data; | ||
134 | if (pcm_channels[dev] < 2) | ||
135 | pcm_channels[dev] = 2; | ||
136 | |||
137 | xirq = irq[dev]; | ||
138 | if (xirq == SNDRV_AUTO_IRQ) { | ||
139 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
140 | snd_card_free(card); | ||
141 | snd_printk("unable to find a free IRQ\n"); | ||
142 | return -EBUSY; | ||
143 | } | ||
144 | } | ||
145 | xdma1 = dma1[dev]; | ||
146 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
147 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
148 | snd_card_free(card); | ||
149 | snd_printk("unable to find a free DMA1\n"); | ||
150 | return -EBUSY; | ||
151 | } | ||
152 | } | ||
153 | xdma2 = dma2[dev]; | ||
154 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
155 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
156 | snd_card_free(card); | ||
157 | snd_printk("unable to find a free DMA2\n"); | ||
158 | return -EBUSY; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | |||
163 | if ((err = snd_gus_create(card, | ||
164 | port[dev], | ||
165 | xirq, xdma1, xdma2, | ||
166 | 0, channels[dev], pcm_channels[dev], | ||
167 | 0, &gus)) < 0) { | ||
168 | snd_card_free(card); | ||
169 | return err; | ||
170 | } | ||
171 | if ((err = snd_gusclassic_detect(gus)) < 0) { | ||
172 | snd_card_free(card); | ||
173 | return err; | ||
174 | } | ||
175 | snd_gusclassic_init(dev, gus); | ||
176 | if ((err = snd_gus_initialize(gus)) < 0) { | ||
177 | snd_card_free(card); | ||
178 | return err; | ||
179 | } | ||
180 | if (gus->max_flag || gus->ess_flag) { | ||
181 | snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port); | ||
182 | snd_card_free(card); | ||
183 | return -ENODEV; | ||
184 | } | ||
185 | if ((err = snd_gf1_new_mixer(gus)) < 0) { | ||
186 | snd_card_free(card); | ||
187 | return err; | ||
188 | } | ||
189 | if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) { | ||
190 | snd_card_free(card); | ||
191 | return err; | ||
192 | } | ||
193 | if (!gus->ace_flag) { | ||
194 | if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { | ||
195 | snd_card_free(card); | ||
196 | return err; | ||
197 | } | ||
198 | } | ||
199 | sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, xirq, xdma1); | ||
200 | if (dma2 >= 0) | ||
201 | sprintf(card->longname + strlen(card->longname), "&%d", xdma2); | ||
202 | if ((err = snd_card_register(card)) < 0) { | ||
203 | snd_card_free(card); | ||
204 | return err; | ||
205 | } | ||
206 | snd_gusclassic_cards[dev] = card; | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int __init snd_gusclassic_legacy_auto_probe(unsigned long xport) | ||
211 | { | ||
212 | static int dev; | ||
213 | int res; | ||
214 | |||
215 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
216 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
217 | continue; | ||
218 | port[dev] = xport; | ||
219 | res = snd_gusclassic_probe(dev); | ||
220 | if (res < 0) | ||
221 | port[dev] = SNDRV_AUTO_PORT; | ||
222 | return res; | ||
223 | } | ||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
227 | static int __init alsa_card_gusclassic_init(void) | ||
228 | { | ||
229 | static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; | ||
230 | int dev, cards, i; | ||
231 | |||
232 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
233 | if (port[dev] == SNDRV_AUTO_PORT) | ||
234 | continue; | ||
235 | if (snd_gusclassic_probe(dev) >= 0) | ||
236 | cards++; | ||
237 | } | ||
238 | i = snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe); | ||
239 | if (i > 0) | ||
240 | cards += i; | ||
241 | |||
242 | if (!cards) { | ||
243 | #ifdef MODULE | ||
244 | printk(KERN_ERR "GUS Classic soundcard not found or device busy\n"); | ||
245 | #endif | ||
246 | return -ENODEV; | ||
247 | } | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static void __exit alsa_card_gusclassic_exit(void) | ||
252 | { | ||
253 | int idx; | ||
254 | |||
255 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
256 | snd_card_free(snd_gusclassic_cards[idx]); | ||
257 | } | ||
258 | |||
259 | module_init(alsa_card_gusclassic_init) | ||
260 | module_exit(alsa_card_gusclassic_exit) | ||
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c new file mode 100644 index 000000000000..bc6fecb18dcf --- /dev/null +++ b/sound/isa/gus/gusextreme.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * Driver for Gravis UltraSound Extreme soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #include <sound/es1688.h> | ||
31 | #include <sound/mpu401.h> | ||
32 | #include <sound/opl3.h> | ||
33 | #define SNDRV_LEGACY_AUTO_PROBE | ||
34 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
35 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
36 | #include <sound/initval.h> | ||
37 | |||
38 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
39 | MODULE_DESCRIPTION("Gravis UltraSound Extreme"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}"); | ||
42 | |||
43 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
44 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
45 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
46 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ | ||
47 | static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */ | ||
48 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */ | ||
49 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
50 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
51 | static int gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ | ||
52 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
53 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
54 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
55 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
56 | static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; | ||
57 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
58 | |||
59 | module_param_array(index, int, NULL, 0444); | ||
60 | MODULE_PARM_DESC(index, "Index value for GUS Extreme soundcard."); | ||
61 | module_param_array(id, charp, NULL, 0444); | ||
62 | MODULE_PARM_DESC(id, "ID string for GUS Extreme soundcard."); | ||
63 | module_param_array(enable, bool, NULL, 0444); | ||
64 | MODULE_PARM_DESC(enable, "Enable GUS Extreme soundcard."); | ||
65 | module_param_array(port, long, NULL, 0444); | ||
66 | MODULE_PARM_DESC(port, "Port # for GUS Extreme driver."); | ||
67 | module_param_array(gf1_port, long, NULL, 0444); | ||
68 | MODULE_PARM_DESC(gf1_port, "GF1 port # for GUS Extreme driver (optional)."); | ||
69 | module_param_array(mpu_port, long, NULL, 0444); | ||
70 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for GUS Extreme driver."); | ||
71 | module_param_array(irq, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(irq, "IRQ # for GUS Extreme driver."); | ||
73 | module_param_array(mpu_irq, int, NULL, 0444); | ||
74 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for GUS Extreme driver."); | ||
75 | module_param_array(gf1_irq, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for GUS Extreme driver."); | ||
77 | module_param_array(dma8, int, NULL, 0444); | ||
78 | MODULE_PARM_DESC(dma8, "8-bit DMA # for GUS Extreme driver."); | ||
79 | module_param_array(dma1, int, NULL, 0444); | ||
80 | MODULE_PARM_DESC(dma1, "GF1 DMA # for GUS Extreme driver."); | ||
81 | module_param_array(joystick_dac, int, NULL, 0444); | ||
82 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver."); | ||
83 | module_param_array(channels, int, NULL, 0444); | ||
84 | MODULE_PARM_DESC(channels, "GF1 channels for GUS Extreme driver."); | ||
85 | module_param_array(pcm_channels, int, NULL, 0444); | ||
86 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Extreme driver."); | ||
87 | |||
88 | static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
89 | |||
90 | |||
91 | static int __init snd_gusextreme_detect(int dev, | ||
92 | snd_card_t * card, | ||
93 | snd_gus_card_t * gus, | ||
94 | es1688_t *es1688) | ||
95 | { | ||
96 | unsigned long flags; | ||
97 | |||
98 | /* | ||
99 | * This is main stuff - enable access to GF1 chip... | ||
100 | * I'm not sure, if this will work for card which have | ||
101 | * ES1688 chip in another place than 0x220. | ||
102 | * | ||
103 | * I used reverse-engineering in DOSEMU. [--jk] | ||
104 | * | ||
105 | * ULTRINIT.EXE: | ||
106 | * 0x230 = 0,2,3 | ||
107 | * 0x240 = 2,0,1 | ||
108 | * 0x250 = 2,0,3 | ||
109 | * 0x260 = 2,2,1 | ||
110 | */ | ||
111 | |||
112 | spin_lock_irqsave(&es1688->mixer_lock, flags); | ||
113 | snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */ | ||
114 | spin_unlock_irqrestore(&es1688->mixer_lock, flags); | ||
115 | spin_lock_irqsave(&es1688->reg_lock, flags); | ||
116 | outb(gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1)); | ||
117 | outb(0, 0x201); | ||
118 | outb(gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1)); | ||
119 | outb(0, 0x201); | ||
120 | outb(gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); | ||
121 | spin_unlock_irqrestore(&es1688->reg_lock, flags); | ||
122 | |||
123 | udelay(100); | ||
124 | |||
125 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
126 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
127 | { | ||
128 | unsigned char d; | ||
129 | |||
130 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
131 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
132 | return -EIO; | ||
133 | } | ||
134 | } | ||
135 | #else | ||
136 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
137 | return -EIO; | ||
138 | #endif | ||
139 | udelay(160); | ||
140 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
141 | udelay(160); | ||
142 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
143 | { | ||
144 | unsigned char d; | ||
145 | |||
146 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
147 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
148 | return -EIO; | ||
149 | } | ||
150 | } | ||
151 | #else | ||
152 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
153 | return -EIO; | ||
154 | #endif | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus) | ||
160 | { | ||
161 | gus->joystick_dac = joystick_dac[dev]; | ||
162 | } | ||
163 | |||
164 | static int __init snd_gusextreme_mixer(es1688_t *chip) | ||
165 | { | ||
166 | snd_card_t *card = chip->card; | ||
167 | snd_ctl_elem_id_t id1, id2; | ||
168 | int err; | ||
169 | |||
170 | memset(&id1, 0, sizeof(id1)); | ||
171 | memset(&id2, 0, sizeof(id2)); | ||
172 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
173 | /* reassign AUX to SYNTHESIZER */ | ||
174 | strcpy(id1.name, "Aux Playback Volume"); | ||
175 | strcpy(id2.name, "Synth Playback Volume"); | ||
176 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
177 | return err; | ||
178 | /* reassign Master Playback Switch to Synth Playback Switch */ | ||
179 | strcpy(id1.name, "Master Playback Switch"); | ||
180 | strcpy(id2.name, "Synth Playback Switch"); | ||
181 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
182 | return err; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int __init snd_gusextreme_probe(int dev) | ||
187 | { | ||
188 | static int possible_ess_irqs[] = {5, 9, 10, 7, -1}; | ||
189 | static int possible_ess_dmas[] = {1, 3, 0, -1}; | ||
190 | static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; | ||
191 | static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1}; | ||
192 | int xgf1_irq, xgf1_dma, xess_irq, xmpu_irq, xess_dma; | ||
193 | snd_card_t *card; | ||
194 | struct snd_gusextreme *acard; | ||
195 | snd_gus_card_t *gus; | ||
196 | es1688_t *es1688; | ||
197 | opl3_t *opl3; | ||
198 | int err; | ||
199 | |||
200 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
201 | if (card == NULL) | ||
202 | return -ENOMEM; | ||
203 | acard = (struct snd_gusextreme *)card->private_data; | ||
204 | |||
205 | xgf1_irq = gf1_irq[dev]; | ||
206 | if (xgf1_irq == SNDRV_AUTO_IRQ) { | ||
207 | if ((xgf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) { | ||
208 | snd_printk("unable to find a free IRQ for GF1\n"); | ||
209 | err = -EBUSY; | ||
210 | goto out; | ||
211 | } | ||
212 | } | ||
213 | xess_irq = irq[dev]; | ||
214 | if (xess_irq == SNDRV_AUTO_IRQ) { | ||
215 | if ((xess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) { | ||
216 | snd_printk("unable to find a free IRQ for ES1688\n"); | ||
217 | err = -EBUSY; | ||
218 | goto out; | ||
219 | } | ||
220 | } | ||
221 | if (mpu_port[dev] == SNDRV_AUTO_PORT) | ||
222 | mpu_port[dev] = 0; | ||
223 | xmpu_irq = mpu_irq[dev]; | ||
224 | if (xmpu_irq > 15) | ||
225 | xmpu_irq = -1; | ||
226 | xgf1_dma = dma1[dev]; | ||
227 | if (xgf1_dma == SNDRV_AUTO_DMA) { | ||
228 | if ((xgf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) { | ||
229 | snd_printk("unable to find a free DMA for GF1\n"); | ||
230 | err = -EBUSY; | ||
231 | goto out; | ||
232 | } | ||
233 | } | ||
234 | xess_dma = dma8[dev]; | ||
235 | if (xess_dma == SNDRV_AUTO_DMA) { | ||
236 | if ((xess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) { | ||
237 | snd_printk("unable to find a free DMA for ES1688\n"); | ||
238 | err = -EBUSY; | ||
239 | goto out; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if ((err = snd_es1688_create(card, port[dev], mpu_port[dev], | ||
244 | xess_irq, xmpu_irq, xess_dma, | ||
245 | ES1688_HW_1688, &es1688)) < 0) | ||
246 | goto out; | ||
247 | if (gf1_port[dev] < 0) | ||
248 | gf1_port[dev] = port[dev] + 0x20; | ||
249 | if ((err = snd_gus_create(card, | ||
250 | gf1_port[dev], | ||
251 | xgf1_irq, | ||
252 | xgf1_dma, | ||
253 | -1, | ||
254 | 0, channels[dev], | ||
255 | pcm_channels[dev], 0, | ||
256 | &gus)) < 0) | ||
257 | goto out; | ||
258 | |||
259 | if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) | ||
260 | goto out; | ||
261 | |||
262 | snd_gusextreme_init(dev, gus); | ||
263 | if ((err = snd_gus_initialize(gus)) < 0) | ||
264 | goto out; | ||
265 | |||
266 | if (!gus->ess_flag) { | ||
267 | snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port); | ||
268 | err = -ENODEV; | ||
269 | goto out; | ||
270 | } | ||
271 | if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) | ||
272 | goto out; | ||
273 | |||
274 | if ((err = snd_es1688_mixer(es1688)) < 0) | ||
275 | goto out; | ||
276 | |||
277 | snd_component_add(card, "ES1688"); | ||
278 | if (pcm_channels[dev] > 0) { | ||
279 | if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) | ||
280 | goto out; | ||
281 | } | ||
282 | if ((err = snd_gf1_new_mixer(gus)) < 0) | ||
283 | goto out; | ||
284 | |||
285 | if ((err = snd_gusextreme_mixer(es1688)) < 0) | ||
286 | goto out; | ||
287 | |||
288 | if (snd_opl3_create(card, es1688->port, es1688->port + 2, | ||
289 | OPL3_HW_OPL3, 0, &opl3) < 0) { | ||
290 | printk(KERN_ERR "gusextreme: opl3 not detected at 0x%lx\n", es1688->port); | ||
291 | } else { | ||
292 | if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) | ||
293 | goto out; | ||
294 | } | ||
295 | |||
296 | if (es1688->mpu_port >= 0x300 && | ||
297 | (err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, | ||
298 | es1688->mpu_port, 0, | ||
299 | xmpu_irq, | ||
300 | SA_INTERRUPT, | ||
301 | NULL)) < 0) | ||
302 | goto out; | ||
303 | |||
304 | sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i", | ||
305 | es1688->port, xgf1_irq, xess_irq, xgf1_dma, xess_dma); | ||
306 | if ((err = snd_card_register(card)) < 0) | ||
307 | goto out; | ||
308 | |||
309 | snd_gusextreme_cards[dev] = card; | ||
310 | return 0; | ||
311 | |||
312 | out: | ||
313 | snd_card_free(card); | ||
314 | return err; | ||
315 | } | ||
316 | |||
317 | static int __init snd_gusextreme_legacy_auto_probe(unsigned long xport) | ||
318 | { | ||
319 | static int dev; | ||
320 | int res; | ||
321 | |||
322 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
323 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
324 | continue; | ||
325 | port[dev] = xport; | ||
326 | res = snd_gusextreme_probe(dev); | ||
327 | if (res < 0) | ||
328 | port[dev] = SNDRV_AUTO_PORT; | ||
329 | return res; | ||
330 | } | ||
331 | return -ENODEV; | ||
332 | } | ||
333 | |||
334 | static int __init alsa_card_gusextreme_init(void) | ||
335 | { | ||
336 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; | ||
337 | int dev, cards, i; | ||
338 | |||
339 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev] > 0; dev++) { | ||
340 | if (port[dev] == SNDRV_AUTO_PORT) | ||
341 | continue; | ||
342 | if (snd_gusextreme_probe(dev) >= 0) | ||
343 | cards++; | ||
344 | } | ||
345 | i = snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe); | ||
346 | if (i > 0) | ||
347 | cards += i; | ||
348 | |||
349 | if (!cards) { | ||
350 | #ifdef MODULE | ||
351 | printk(KERN_ERR "GUS Extreme soundcard not found or device busy\n"); | ||
352 | #endif | ||
353 | return -ENODEV; | ||
354 | } | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static void __exit alsa_card_gusextreme_exit(void) | ||
359 | { | ||
360 | int idx; | ||
361 | snd_card_t *card; | ||
362 | struct snd_gusextreme *acard; | ||
363 | |||
364 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | ||
365 | card = snd_gusextreme_cards[idx]; | ||
366 | if (card == NULL) | ||
367 | continue; | ||
368 | acard = (struct snd_gusextreme *)card->private_data; | ||
369 | snd_card_free(snd_gusextreme_cards[idx]); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | module_init(alsa_card_gusextreme_init) | ||
374 | module_exit(alsa_card_gusextreme_exit) | ||
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c new file mode 100644 index 000000000000..400ff34710fb --- /dev/null +++ b/sound/isa/gus/gusmax.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * Driver for Gravis UltraSound MAX soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #include <sound/cs4231.h> | ||
31 | #define SNDRV_LEGACY_AUTO_PROBE | ||
32 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
33 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
34 | #include <sound/initval.h> | ||
35 | |||
36 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
37 | MODULE_DESCRIPTION("Gravis UltraSound MAX"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound MAX}}"); | ||
40 | |||
41 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
42 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
43 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
44 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ | ||
45 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ | ||
46 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
47 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
48 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
49 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
50 | static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; | ||
51 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
52 | |||
53 | module_param_array(index, int, NULL, 0444); | ||
54 | MODULE_PARM_DESC(index, "Index value for GUS MAX soundcard."); | ||
55 | module_param_array(id, charp, NULL, 0444); | ||
56 | MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard."); | ||
57 | module_param_array(enable, bool, NULL, 0444); | ||
58 | MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard."); | ||
59 | module_param_array(port, long, NULL, 0444); | ||
60 | MODULE_PARM_DESC(port, "Port # for GUS MAX driver."); | ||
61 | module_param_array(irq, int, NULL, 0444); | ||
62 | MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver."); | ||
63 | module_param_array(dma1, int, NULL, 0444); | ||
64 | MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver."); | ||
65 | module_param_array(dma2, int, NULL, 0444); | ||
66 | MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver."); | ||
67 | module_param_array(joystick_dac, int, NULL, 0444); | ||
68 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); | ||
69 | module_param_array(channels, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(channels, "Used GF1 channels for GUS MAX driver."); | ||
71 | module_param_array(pcm_channels, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS MAX driver."); | ||
73 | |||
74 | struct snd_gusmax { | ||
75 | int irq; | ||
76 | snd_card_t *card; | ||
77 | snd_gus_card_t *gus; | ||
78 | cs4231_t *cs4231; | ||
79 | unsigned short gus_status_reg; | ||
80 | unsigned short pcm_status_reg; | ||
81 | }; | ||
82 | |||
83 | static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
84 | |||
85 | |||
86 | static int __init snd_gusmax_detect(snd_gus_card_t * gus) | ||
87 | { | ||
88 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
89 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
90 | { | ||
91 | unsigned char d; | ||
92 | |||
93 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
94 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
95 | return -ENODEV; | ||
96 | } | ||
97 | } | ||
98 | #else | ||
99 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
100 | return -ENODEV; | ||
101 | #endif | ||
102 | udelay(160); | ||
103 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
104 | udelay(160); | ||
105 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
106 | { | ||
107 | unsigned char d; | ||
108 | |||
109 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
110 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
111 | return -ENODEV; | ||
112 | } | ||
113 | } | ||
114 | #else | ||
115 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
116 | return -ENODEV; | ||
117 | #endif | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
122 | { | ||
123 | struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id; | ||
124 | int loop, max = 5; | ||
125 | int handled = 0; | ||
126 | |||
127 | do { | ||
128 | loop = 0; | ||
129 | if (inb(maxcard->gus_status_reg)) { | ||
130 | handled = 1; | ||
131 | snd_gus_interrupt(irq, maxcard->gus, regs); | ||
132 | loop++; | ||
133 | } | ||
134 | if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ | ||
135 | handled = 1; | ||
136 | snd_cs4231_interrupt(irq, maxcard->cs4231, regs); | ||
137 | loop++; | ||
138 | } | ||
139 | } while (loop && --max > 0); | ||
140 | return IRQ_RETVAL(handled); | ||
141 | } | ||
142 | |||
143 | static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus) | ||
144 | { | ||
145 | gus->equal_irq = 1; | ||
146 | gus->codec_flag = 1; | ||
147 | gus->joystick_dac = joystick_dac[dev]; | ||
148 | /* init control register */ | ||
149 | gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f; | ||
150 | if (gus->gf1.dma1 > 3) | ||
151 | gus->max_cntrl_val |= 0x10; | ||
152 | if (gus->gf1.dma2 > 3) | ||
153 | gus->max_cntrl_val |= 0x20; | ||
154 | gus->max_cntrl_val |= 0x40; | ||
155 | outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT)); | ||
156 | } | ||
157 | |||
158 | #define CS4231_PRIVATE( left, right, shift, mute ) \ | ||
159 | ((left << 24)|(right << 16)|(shift<<8)|mute) | ||
160 | |||
161 | static int __init snd_gusmax_mixer(cs4231_t *chip) | ||
162 | { | ||
163 | snd_card_t *card = chip->card; | ||
164 | snd_ctl_elem_id_t id1, id2; | ||
165 | int err; | ||
166 | |||
167 | memset(&id1, 0, sizeof(id1)); | ||
168 | memset(&id2, 0, sizeof(id2)); | ||
169 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
170 | /* reassign AUXA to SYNTHESIZER */ | ||
171 | strcpy(id1.name, "Aux Playback Switch"); | ||
172 | strcpy(id2.name, "Synth Playback Switch"); | ||
173 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
174 | return err; | ||
175 | strcpy(id1.name, "Aux Playback Volume"); | ||
176 | strcpy(id2.name, "Synth Playback Volume"); | ||
177 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
178 | return err; | ||
179 | /* reassign AUXB to CD */ | ||
180 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
181 | strcpy(id2.name, "CD Playback Switch"); | ||
182 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
183 | return err; | ||
184 | strcpy(id1.name, "Aux Playback Volume"); | ||
185 | strcpy(id2.name, "CD Playback Volume"); | ||
186 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
187 | return err; | ||
188 | #if 0 | ||
189 | /* reassign Mono Input to MIC */ | ||
190 | if (snd_mixer_group_rename(mixer, | ||
191 | SNDRV_MIXER_IN_MONO, 0, | ||
192 | SNDRV_MIXER_IN_MIC, 0) < 0) | ||
193 | goto __error; | ||
194 | if (snd_mixer_elem_rename(mixer, | ||
195 | SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT, | ||
196 | SNDRV_MIXER_IN_MIC, 0) < 0) | ||
197 | goto __error; | ||
198 | if (snd_mixer_elem_rename(mixer, | ||
199 | "Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1, | ||
200 | "Mic Capture Volume", 0) < 0) | ||
201 | goto __error; | ||
202 | if (snd_mixer_elem_rename(mixer, | ||
203 | "Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1, | ||
204 | "Mic Capture Switch", 0) < 0) | ||
205 | goto __error; | ||
206 | #endif | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static void snd_gusmax_free(snd_card_t *card) | ||
211 | { | ||
212 | struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; | ||
213 | |||
214 | if (maxcard == NULL) | ||
215 | return; | ||
216 | if (maxcard->irq >= 0) | ||
217 | free_irq(maxcard->irq, (void *)maxcard); | ||
218 | } | ||
219 | |||
220 | static int __init snd_gusmax_probe(int dev) | ||
221 | { | ||
222 | static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; | ||
223 | static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; | ||
224 | int xirq, xdma1, xdma2, err; | ||
225 | snd_card_t *card; | ||
226 | snd_gus_card_t *gus = NULL; | ||
227 | cs4231_t *cs4231; | ||
228 | struct snd_gusmax *maxcard; | ||
229 | |||
230 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
231 | sizeof(struct snd_gusmax)); | ||
232 | if (card == NULL) | ||
233 | return -ENOMEM; | ||
234 | card->private_free = snd_gusmax_free; | ||
235 | maxcard = (struct snd_gusmax *)card->private_data; | ||
236 | maxcard->card = card; | ||
237 | maxcard->irq = -1; | ||
238 | |||
239 | xirq = irq[dev]; | ||
240 | if (xirq == SNDRV_AUTO_IRQ) { | ||
241 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
242 | snd_card_free(card); | ||
243 | snd_printk("unable to find a free IRQ\n"); | ||
244 | return -EBUSY; | ||
245 | } | ||
246 | } | ||
247 | xdma1 = dma1[dev]; | ||
248 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
249 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
250 | snd_card_free(card); | ||
251 | snd_printk("unable to find a free DMA1\n"); | ||
252 | return -EBUSY; | ||
253 | } | ||
254 | } | ||
255 | xdma2 = dma2[dev]; | ||
256 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
257 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
258 | snd_card_free(card); | ||
259 | snd_printk("unable to find a free DMA2\n"); | ||
260 | return -EBUSY; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | if ((err = snd_gus_create(card, | ||
265 | port[dev], | ||
266 | -xirq, xdma1, xdma2, | ||
267 | 0, channels[dev], | ||
268 | pcm_channels[dev], | ||
269 | 0, &gus)) < 0) { | ||
270 | snd_card_free(card); | ||
271 | return err; | ||
272 | } | ||
273 | if ((err = snd_gusmax_detect(gus)) < 0) { | ||
274 | snd_card_free(card); | ||
275 | return err; | ||
276 | } | ||
277 | maxcard->gus_status_reg = gus->gf1.reg_irqstat; | ||
278 | maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; | ||
279 | snd_gusmax_init(dev, card, gus); | ||
280 | if ((err = snd_gus_initialize(gus)) < 0) { | ||
281 | snd_card_free(card); | ||
282 | return err; | ||
283 | } | ||
284 | if (!gus->max_flag) { | ||
285 | printk(KERN_ERR "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); | ||
286 | snd_card_free(card); | ||
287 | return -ENODEV; | ||
288 | } | ||
289 | |||
290 | if (request_irq(xirq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { | ||
291 | snd_card_free(card); | ||
292 | printk(KERN_ERR "gusmax: unable to grab IRQ %d\n", xirq); | ||
293 | return -EBUSY; | ||
294 | } | ||
295 | maxcard->irq = xirq; | ||
296 | |||
297 | if ((err = snd_cs4231_create(card, | ||
298 | gus->gf1.port + 0x10c, -1, xirq, | ||
299 | xdma2 < 0 ? xdma1 : xdma2, xdma1, | ||
300 | CS4231_HW_DETECT, | ||
301 | CS4231_HWSHARE_IRQ | | ||
302 | CS4231_HWSHARE_DMA1 | | ||
303 | CS4231_HWSHARE_DMA2, | ||
304 | &cs4231)) < 0) { | ||
305 | snd_card_free(card); | ||
306 | return err; | ||
307 | } | ||
308 | if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) { | ||
309 | snd_card_free(card); | ||
310 | return err; | ||
311 | } | ||
312 | if ((err = snd_cs4231_mixer(cs4231)) < 0) { | ||
313 | snd_card_free(card); | ||
314 | return err; | ||
315 | } | ||
316 | if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { | ||
317 | snd_card_free(card); | ||
318 | return err; | ||
319 | } | ||
320 | if (pcm_channels[dev] > 0) { | ||
321 | if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { | ||
322 | snd_card_free(card); | ||
323 | return err; | ||
324 | } | ||
325 | } | ||
326 | if ((err = snd_gusmax_mixer(cs4231)) < 0) { | ||
327 | snd_card_free(card); | ||
328 | return err; | ||
329 | } | ||
330 | |||
331 | if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { | ||
332 | snd_card_free(card); | ||
333 | return err; | ||
334 | } | ||
335 | |||
336 | sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1); | ||
337 | if (xdma2 >= 0) | ||
338 | sprintf(card->longname + strlen(card->longname), "&%i", xdma2); | ||
339 | if ((err = snd_card_register(card)) < 0) { | ||
340 | snd_card_free(card); | ||
341 | return err; | ||
342 | } | ||
343 | |||
344 | maxcard->gus = gus; | ||
345 | maxcard->cs4231 = cs4231; | ||
346 | snd_gusmax_cards[dev] = card; | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int __init snd_gusmax_legacy_auto_probe(unsigned long xport) | ||
351 | { | ||
352 | static int dev; | ||
353 | int res; | ||
354 | |||
355 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
356 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
357 | continue; | ||
358 | port[dev] = xport; | ||
359 | res = snd_gusmax_probe(dev); | ||
360 | if (res < 0) | ||
361 | port[dev] = SNDRV_AUTO_PORT; | ||
362 | return res; | ||
363 | } | ||
364 | return -ENODEV; | ||
365 | } | ||
366 | |||
367 | static int __init alsa_card_gusmax_init(void) | ||
368 | { | ||
369 | static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; | ||
370 | int dev, cards, i; | ||
371 | |||
372 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev] > 0; dev++) { | ||
373 | if (port[dev] == SNDRV_AUTO_PORT) | ||
374 | continue; | ||
375 | if (snd_gusmax_probe(dev) >= 0) | ||
376 | cards++; | ||
377 | } | ||
378 | i = snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe); | ||
379 | if (i > 0) | ||
380 | cards += i; | ||
381 | |||
382 | if (!cards) { | ||
383 | #ifdef MODULE | ||
384 | printk(KERN_ERR "GUS MAX soundcard not found or device busy\n"); | ||
385 | #endif | ||
386 | return -ENODEV; | ||
387 | } | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | static void __exit alsa_card_gusmax_exit(void) | ||
392 | { | ||
393 | int idx; | ||
394 | |||
395 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
396 | snd_card_free(snd_gusmax_cards[idx]); | ||
397 | } | ||
398 | |||
399 | module_init(alsa_card_gusmax_init) | ||
400 | module_exit(alsa_card_gusmax_exit) | ||
diff --git a/sound/isa/gus/interwave-stb.c b/sound/isa/gus/interwave-stb.c new file mode 100644 index 000000000000..dbe4f48a9846 --- /dev/null +++ b/sound/isa/gus/interwave-stb.c | |||
@@ -0,0 +1,2 @@ | |||
1 | #define SNDRV_STB | ||
2 | #include "interwave.c" | ||
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c new file mode 100644 index 000000000000..46e867daba6a --- /dev/null +++ b/sound/isa/gus/interwave.c | |||
@@ -0,0 +1,969 @@ | |||
1 | /* | ||
2 | * Driver for AMD InterWave soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | * 1999/07/22 Erik Inge Bolso <knan@mo.himolde.no> | ||
21 | * * mixer group handlers | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <sound/driver.h> | ||
26 | #include <asm/dma.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/pnp.h> | ||
31 | #include <linux/moduleparam.h> | ||
32 | #include <sound/core.h> | ||
33 | #include <sound/gus.h> | ||
34 | #include <sound/cs4231.h> | ||
35 | #ifdef SNDRV_STB | ||
36 | #include <sound/tea6330t.h> | ||
37 | #endif | ||
38 | #define SNDRV_LEGACY_AUTO_PROBE | ||
39 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
40 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
41 | #include <sound/initval.h> | ||
42 | |||
43 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | #ifndef SNDRV_STB | ||
46 | MODULE_DESCRIPTION("AMD InterWave"); | ||
47 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Plug & Play}," | ||
48 | "{STB,SoundRage32}," | ||
49 | "{MED,MED3210}," | ||
50 | "{Dynasonix,Dynasonix Pro}," | ||
51 | "{Panasonic,PCA761AW}}"); | ||
52 | #else | ||
53 | MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); | ||
54 | MODULE_SUPPORTED_DEVICE("{{AMD,InterWave STB with TEA6330T}}"); | ||
55 | #endif | ||
56 | |||
57 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
58 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
59 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
60 | #ifdef CONFIG_PNP | ||
61 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
62 | #endif | ||
63 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */ | ||
64 | #ifdef SNDRV_STB | ||
65 | static long port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */ | ||
66 | #endif | ||
67 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ | ||
68 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
69 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
70 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
71 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
72 | static int midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; | ||
73 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
74 | static int effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; | ||
75 | |||
76 | module_param_array(index, int, NULL, 0444); | ||
77 | MODULE_PARM_DESC(index, "Index value for InterWave soundcard."); | ||
78 | module_param_array(id, charp, NULL, 0444); | ||
79 | MODULE_PARM_DESC(id, "ID string for InterWave soundcard."); | ||
80 | module_param_array(enable, bool, NULL, 0444); | ||
81 | MODULE_PARM_DESC(enable, "Enable InterWave soundcard."); | ||
82 | #ifdef CONFIG_PNP | ||
83 | module_param_array(isapnp, bool, NULL, 0444); | ||
84 | MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); | ||
85 | #endif | ||
86 | module_param_array(port, long, NULL, 0444); | ||
87 | MODULE_PARM_DESC(port, "Port # for InterWave driver."); | ||
88 | #ifdef SNDRV_STB | ||
89 | module_param_array(port_tc, long, NULL, 0444); | ||
90 | MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); | ||
91 | #endif | ||
92 | module_param_array(irq, int, NULL, 0444); | ||
93 | MODULE_PARM_DESC(irq, "IRQ # for InterWave driver."); | ||
94 | module_param_array(dma1, int, NULL, 0444); | ||
95 | MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver."); | ||
96 | module_param_array(dma2, int, NULL, 0444); | ||
97 | MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver."); | ||
98 | module_param_array(joystick_dac, int, NULL, 0444); | ||
99 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); | ||
100 | module_param_array(midi, int, NULL, 0444); | ||
101 | MODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver."); | ||
102 | module_param_array(pcm_channels, int, NULL, 0444); | ||
103 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver."); | ||
104 | module_param_array(effect, int, NULL, 0444); | ||
105 | MODULE_PARM_DESC(effect, "Effects enable for InterWave driver."); | ||
106 | |||
107 | struct snd_interwave { | ||
108 | int irq; | ||
109 | snd_card_t *card; | ||
110 | snd_gus_card_t *gus; | ||
111 | cs4231_t *cs4231; | ||
112 | #ifdef SNDRV_STB | ||
113 | struct resource *i2c_res; | ||
114 | #endif | ||
115 | unsigned short gus_status_reg; | ||
116 | unsigned short pcm_status_reg; | ||
117 | #ifdef CONFIG_PNP | ||
118 | struct pnp_dev *dev; | ||
119 | #ifdef SNDRV_STB | ||
120 | struct pnp_dev *devtc; | ||
121 | #endif | ||
122 | #endif | ||
123 | }; | ||
124 | |||
125 | static snd_card_t *snd_interwave_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
126 | |||
127 | #ifdef CONFIG_PNP | ||
128 | |||
129 | static struct pnp_card_device_id snd_interwave_pnpids[] = { | ||
130 | #ifndef SNDRV_STB | ||
131 | /* Gravis UltraSound Plug & Play */ | ||
132 | { .id = "GRV0001", .devs = { { .id = "GRV0000" } } }, | ||
133 | /* STB SoundRage32 */ | ||
134 | { .id = "STB011a", .devs = { { .id = "STB0010" } } }, | ||
135 | /* MED3210 */ | ||
136 | { .id = "DXP3201", .devs = { { .id = "DXP0010" } } }, | ||
137 | /* Dynasonic Pro */ | ||
138 | /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ | ||
139 | { .id = "CDC1111", .devs = { { .id = "CDC1112" } } }, | ||
140 | /* Panasonic PCA761AW Audio Card */ | ||
141 | { .id = "ADV55ff", .devs = { { .id = "ADV0010" } } }, | ||
142 | /* InterWave STB without TEA6330T */ | ||
143 | { .id = "ADV550a", .devs = { { .id = "ADV0010" } } }, | ||
144 | #else | ||
145 | /* InterWave STB with TEA6330T */ | ||
146 | { .id = "ADV550a", .devs = { { .id = "ADV0010" }, { .id = "ADV0015" } } }, | ||
147 | #endif | ||
148 | { .id = "" } | ||
149 | }; | ||
150 | |||
151 | MODULE_DEVICE_TABLE(pnp_card, snd_interwave_pnpids); | ||
152 | |||
153 | #endif /* CONFIG_PNP */ | ||
154 | |||
155 | |||
156 | #ifdef SNDRV_STB | ||
157 | static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data) | ||
158 | { | ||
159 | unsigned long port = bus->private_value; | ||
160 | |||
161 | #if 0 | ||
162 | printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data); | ||
163 | #endif | ||
164 | outb((data << 1) | ctrl, port); | ||
165 | udelay(10); | ||
166 | } | ||
167 | |||
168 | static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus) | ||
169 | { | ||
170 | unsigned long port = bus->private_value; | ||
171 | unsigned char res; | ||
172 | |||
173 | res = inb(port) & 1; | ||
174 | #if 0 | ||
175 | printk("i2c_getclockline - 0x%lx -> %i\n", port, res); | ||
176 | #endif | ||
177 | return res; | ||
178 | } | ||
179 | |||
180 | static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack) | ||
181 | { | ||
182 | unsigned long port = bus->private_value; | ||
183 | unsigned char res; | ||
184 | |||
185 | if (ack) | ||
186 | udelay(10); | ||
187 | res = (inb(port) & 2) >> 1; | ||
188 | #if 0 | ||
189 | printk("i2c_getdataline - 0x%lx -> %i\n", port, res); | ||
190 | #endif | ||
191 | return res; | ||
192 | } | ||
193 | |||
194 | static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = { | ||
195 | .setlines = snd_interwave_i2c_setlines, | ||
196 | .getclock = snd_interwave_i2c_getclockline, | ||
197 | .getdata = snd_interwave_i2c_getdataline, | ||
198 | }; | ||
199 | |||
200 | static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard, | ||
201 | snd_gus_card_t * gus, int dev, | ||
202 | snd_i2c_bus_t **rbus) | ||
203 | { | ||
204 | unsigned long port; | ||
205 | snd_i2c_bus_t *bus; | ||
206 | snd_card_t *card = iwcard->card; | ||
207 | char name[32]; | ||
208 | int err; | ||
209 | |||
210 | *rbus = NULL; | ||
211 | port = port_tc[dev]; | ||
212 | if (port == SNDRV_AUTO_PORT) { | ||
213 | port = 0x350; | ||
214 | if (gus->gf1.port == 0x250) { | ||
215 | port = 0x360; | ||
216 | } | ||
217 | while (port <= 0x380) { | ||
218 | if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) | ||
219 | break; | ||
220 | port += 0x10; | ||
221 | } | ||
222 | } else { | ||
223 | iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)"); | ||
224 | } | ||
225 | if (iwcard->i2c_res == NULL) { | ||
226 | snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n"); | ||
227 | return -ENODEV; | ||
228 | } | ||
229 | |||
230 | sprintf(name, "InterWave-%i", card->number); | ||
231 | if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0) | ||
232 | return err; | ||
233 | bus->private_value = port; | ||
234 | bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; | ||
235 | if ((err = snd_tea6330t_detect(bus, 0)) < 0) | ||
236 | return err; | ||
237 | *rbus = bus; | ||
238 | return 0; | ||
239 | } | ||
240 | #endif | ||
241 | |||
242 | static int __devinit snd_interwave_detect(struct snd_interwave *iwcard, | ||
243 | snd_gus_card_t * gus, | ||
244 | int dev | ||
245 | #ifdef SNDRV_STB | ||
246 | , snd_i2c_bus_t **rbus | ||
247 | #endif | ||
248 | ) | ||
249 | { | ||
250 | unsigned long flags; | ||
251 | unsigned char rev1, rev2; | ||
252 | |||
253 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
254 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
255 | { | ||
256 | int d; | ||
257 | |||
258 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
259 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
260 | return -ENODEV; | ||
261 | } | ||
262 | } | ||
263 | #else | ||
264 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
265 | return -ENODEV; | ||
266 | #endif | ||
267 | udelay(160); | ||
268 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
269 | udelay(160); | ||
270 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
271 | { | ||
272 | int d; | ||
273 | |||
274 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
275 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
276 | return -ENODEV; | ||
277 | } | ||
278 | } | ||
279 | #else | ||
280 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
281 | return -ENODEV; | ||
282 | #endif | ||
283 | |||
284 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
285 | rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); | ||
286 | snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); | ||
287 | rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); | ||
288 | snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); | ||
289 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
290 | snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); | ||
291 | if ((rev1 & 0xf0) == (rev2 & 0xf0) && | ||
292 | (rev1 & 0x0f) != (rev2 & 0x0f)) { | ||
293 | snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); | ||
294 | gus->interwave = 1; | ||
295 | strcpy(gus->card->shortname, "AMD InterWave"); | ||
296 | gus->revision = rev1 >> 4; | ||
297 | #ifndef SNDRV_STB | ||
298 | return 0; /* ok.. We have an InterWave board */ | ||
299 | #else | ||
300 | return snd_interwave_detect_stb(iwcard, gus, dev, rbus); | ||
301 | #endif | ||
302 | } | ||
303 | snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | |||
307 | static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
308 | { | ||
309 | struct snd_interwave *iwcard = (struct snd_interwave *) dev_id; | ||
310 | int loop, max = 5; | ||
311 | int handled = 0; | ||
312 | |||
313 | do { | ||
314 | loop = 0; | ||
315 | if (inb(iwcard->gus_status_reg)) { | ||
316 | handled = 1; | ||
317 | snd_gus_interrupt(irq, iwcard->gus, regs); | ||
318 | loop++; | ||
319 | } | ||
320 | if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ | ||
321 | handled = 1; | ||
322 | snd_cs4231_interrupt(irq, iwcard->cs4231, regs); | ||
323 | loop++; | ||
324 | } | ||
325 | } while (loop && --max > 0); | ||
326 | return IRQ_RETVAL(handled); | ||
327 | } | ||
328 | |||
329 | static void __devinit snd_interwave_reset(snd_gus_card_t * gus) | ||
330 | { | ||
331 | snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); | ||
332 | udelay(160); | ||
333 | snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); | ||
334 | udelay(160); | ||
335 | } | ||
336 | |||
337 | static void __devinit snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes) | ||
338 | { | ||
339 | unsigned int idx; | ||
340 | unsigned int local; | ||
341 | unsigned char d; | ||
342 | |||
343 | for (idx = 0; idx < 4; idx++) { | ||
344 | sizes[idx] = 0; | ||
345 | d = 0x55; | ||
346 | for (local = idx << 22; | ||
347 | local < (idx << 22) + 0x400000; | ||
348 | local += 0x40000, d++) { | ||
349 | snd_gf1_poke(gus, local, d); | ||
350 | snd_gf1_poke(gus, local + 1, d + 1); | ||
351 | #if 0 | ||
352 | printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n", | ||
353 | d, | ||
354 | snd_gf1_peek(gus, local), | ||
355 | snd_gf1_peek(gus, local + 1), | ||
356 | snd_gf1_peek(gus, idx << 22)); | ||
357 | #endif | ||
358 | if (snd_gf1_peek(gus, local) != d || | ||
359 | snd_gf1_peek(gus, local + 1) != d + 1 || | ||
360 | snd_gf1_peek(gus, idx << 22) != 0x55) | ||
361 | break; | ||
362 | sizes[idx]++; | ||
363 | } | ||
364 | } | ||
365 | #if 0 | ||
366 | printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]); | ||
367 | #endif | ||
368 | } | ||
369 | |||
370 | struct rom_hdr { | ||
371 | /* 000 */ unsigned char iwave[8]; | ||
372 | /* 008 */ unsigned char rom_hdr_revision; | ||
373 | /* 009 */ unsigned char series_number; | ||
374 | /* 010 */ unsigned char series_name[16]; | ||
375 | /* 026 */ unsigned char date[10]; | ||
376 | /* 036 */ unsigned short vendor_revision_major; | ||
377 | /* 038 */ unsigned short vendor_revision_minor; | ||
378 | /* 040 */ unsigned int rom_size; | ||
379 | /* 044 */ unsigned char copyright[128]; | ||
380 | /* 172 */ unsigned char vendor_name[64]; | ||
381 | /* 236 */ unsigned char rom_description[128]; | ||
382 | /* 364 */ unsigned char pad[147]; | ||
383 | /* 511 */ unsigned char csum; | ||
384 | }; | ||
385 | |||
386 | static void __devinit snd_interwave_detect_memory(snd_gus_card_t * gus) | ||
387 | { | ||
388 | static unsigned int lmc[13] = | ||
389 | { | ||
390 | 0x00000001, 0x00000101, 0x01010101, 0x00000401, | ||
391 | 0x04040401, 0x00040101, 0x04040101, 0x00000004, | ||
392 | 0x00000404, 0x04040404, 0x00000010, 0x00001010, | ||
393 | 0x10101010 | ||
394 | }; | ||
395 | |||
396 | int bank_pos, pages; | ||
397 | unsigned int i, lmct; | ||
398 | int psizes[4]; | ||
399 | unsigned char iwave[8]; | ||
400 | unsigned char csum; | ||
401 | |||
402 | snd_interwave_reset(gus); | ||
403 | snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ | ||
404 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ | ||
405 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); | ||
406 | /* ok.. simple test of memory size */ | ||
407 | pages = 0; | ||
408 | snd_gf1_poke(gus, 0, 0x55); | ||
409 | snd_gf1_poke(gus, 1, 0xaa); | ||
410 | #if 1 | ||
411 | if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa) | ||
412 | #else | ||
413 | if (0) /* ok.. for testing of 0k RAM */ | ||
414 | #endif | ||
415 | { | ||
416 | snd_interwave_bank_sizes(gus, psizes); | ||
417 | lmct = (psizes[3] << 24) | (psizes[2] << 16) | | ||
418 | (psizes[1] << 8) | psizes[0]; | ||
419 | #if 0 | ||
420 | printk("lmct = 0x%08x\n", lmct); | ||
421 | #endif | ||
422 | for (i = 0; i < ARRAY_SIZE(lmc); i++) | ||
423 | if (lmct == lmc[i]) { | ||
424 | #if 0 | ||
425 | printk("found !!! %i\n", i); | ||
426 | #endif | ||
427 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); | ||
428 | snd_interwave_bank_sizes(gus, psizes); | ||
429 | break; | ||
430 | } | ||
431 | if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) | ||
432 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); | ||
433 | for (i = 0; i < 4; i++) { | ||
434 | gus->gf1.mem_alloc.banks_8[i].address = | ||
435 | gus->gf1.mem_alloc.banks_16[i].address = i << 22; | ||
436 | gus->gf1.mem_alloc.banks_8[i].size = | ||
437 | gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; | ||
438 | pages += psizes[i]; | ||
439 | } | ||
440 | } | ||
441 | pages <<= 18; | ||
442 | gus->gf1.memory = pages; | ||
443 | |||
444 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ | ||
445 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); | ||
446 | gus->gf1.rom_banks = 0; | ||
447 | gus->gf1.rom_memory = 0; | ||
448 | for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { | ||
449 | for (i = 0; i < 8; ++i) | ||
450 | iwave[i] = snd_gf1_peek(gus, bank_pos + i); | ||
451 | #ifdef CONFIG_SND_DEBUG_ROM | ||
452 | printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, | ||
453 | iwave[0], iwave[1], iwave[2], iwave[3], | ||
454 | iwave[4], iwave[5], iwave[6], iwave[7]); | ||
455 | #endif | ||
456 | if (strncmp(iwave, "INTRWAVE", 8)) | ||
457 | continue; /* first check */ | ||
458 | csum = 0; | ||
459 | for (i = 0; i < sizeof(struct rom_hdr); i++) | ||
460 | csum += snd_gf1_peek(gus, bank_pos + i); | ||
461 | #ifdef CONFIG_SND_DEBUG_ROM | ||
462 | printk("ROM checksum = 0x%x (computed)\n", csum); | ||
463 | #endif | ||
464 | if (csum != 0) | ||
465 | continue; /* not valid rom */ | ||
466 | gus->gf1.rom_banks++; | ||
467 | gus->gf1.rom_present |= 1 << (bank_pos >> 22); | ||
468 | gus->gf1.rom_memory = snd_gf1_peek(gus, bank_pos + 40) | | ||
469 | (snd_gf1_peek(gus, bank_pos + 41) << 8) | | ||
470 | (snd_gf1_peek(gus, bank_pos + 42) << 16) | | ||
471 | (snd_gf1_peek(gus, bank_pos + 43) << 24); | ||
472 | } | ||
473 | #if 0 | ||
474 | if (gus->gf1.rom_memory > 0) { | ||
475 | if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) | ||
476 | gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; | ||
477 | } | ||
478 | #endif | ||
479 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ | ||
480 | |||
481 | if (!gus->gf1.enh_mode) | ||
482 | snd_interwave_reset(gus); | ||
483 | } | ||
484 | |||
485 | static void __devinit snd_interwave_init(int dev, snd_gus_card_t * gus) | ||
486 | { | ||
487 | unsigned long flags; | ||
488 | |||
489 | /* ok.. some InterWave specific initialization */ | ||
490 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
491 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); | ||
492 | snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); | ||
493 | snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); | ||
494 | snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); | ||
495 | snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); | ||
496 | snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); | ||
497 | snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); | ||
498 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
499 | gus->equal_irq = 1; | ||
500 | gus->codec_flag = 1; | ||
501 | gus->interwave = 1; | ||
502 | gus->max_flag = 1; | ||
503 | gus->joystick_dac = joystick_dac[dev]; | ||
504 | |||
505 | } | ||
506 | |||
507 | static snd_kcontrol_new_t snd_interwave_controls[] = { | ||
508 | CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
509 | CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), | ||
510 | CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), | ||
511 | CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) | ||
512 | }; | ||
513 | |||
514 | static int __devinit snd_interwave_mixer(cs4231_t *chip) | ||
515 | { | ||
516 | snd_card_t *card = chip->card; | ||
517 | snd_ctl_elem_id_t id1, id2; | ||
518 | unsigned int idx; | ||
519 | int err; | ||
520 | |||
521 | memset(&id1, 0, sizeof(id1)); | ||
522 | memset(&id2, 0, sizeof(id2)); | ||
523 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
524 | #if 0 | ||
525 | /* remove mono microphone controls */ | ||
526 | strcpy(id1.name, "Mic Playback Switch"); | ||
527 | if ((err = snd_ctl_remove_id(card, &id1)) < 0) | ||
528 | return err; | ||
529 | strcpy(id1.name, "Mic Playback Volume"); | ||
530 | if ((err = snd_ctl_remove_id(card, &id1)) < 0) | ||
531 | return err; | ||
532 | #endif | ||
533 | /* add new master and mic controls */ | ||
534 | for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) | ||
535 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0) | ||
536 | return err; | ||
537 | snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); | ||
538 | snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); | ||
539 | snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); | ||
540 | snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); | ||
541 | /* reassign AUXA to SYNTHESIZER */ | ||
542 | strcpy(id1.name, "Aux Playback Switch"); | ||
543 | strcpy(id2.name, "Synth Playback Switch"); | ||
544 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
545 | return err; | ||
546 | strcpy(id1.name, "Aux Playback Volume"); | ||
547 | strcpy(id2.name, "Synth Playback Volume"); | ||
548 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
549 | return err; | ||
550 | /* reassign AUXB to CD */ | ||
551 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
552 | strcpy(id2.name, "CD Playback Switch"); | ||
553 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
554 | return err; | ||
555 | strcpy(id1.name, "Aux Playback Volume"); | ||
556 | strcpy(id2.name, "CD Playback Volume"); | ||
557 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
558 | return err; | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | #ifdef CONFIG_PNP | ||
563 | |||
564 | static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard, | ||
565 | struct pnp_card_link *card, | ||
566 | const struct pnp_card_device_id *id) | ||
567 | { | ||
568 | struct pnp_dev *pdev; | ||
569 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
570 | int err; | ||
571 | |||
572 | iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
573 | if (iwcard->dev == NULL) { | ||
574 | kfree(cfg); | ||
575 | return -EBUSY; | ||
576 | } | ||
577 | #ifdef SNDRV_STB | ||
578 | iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
579 | if (iwcard->devtc == NULL) { | ||
580 | kfree(cfg); | ||
581 | return -EBUSY; | ||
582 | } | ||
583 | #endif | ||
584 | /* Synth & Codec initialization */ | ||
585 | pdev = iwcard->dev; | ||
586 | pnp_init_resource_table(cfg); | ||
587 | if (port[dev] != SNDRV_AUTO_PORT) { | ||
588 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
589 | pnp_resource_change(&cfg->port_resource[1], port[dev] + 0x100, 12); | ||
590 | pnp_resource_change(&cfg->port_resource[2], port[dev] + 0x10c, 4); | ||
591 | } | ||
592 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
593 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
594 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
595 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
596 | if (dma2[dev] < 0) | ||
597 | pnp_resource_change(&cfg->dma_resource[1], 4, 1); | ||
598 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
599 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
600 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
601 | snd_printk(KERN_ERR "InterWave - Synth - the requested resources are invalid, using auto config\n"); | ||
602 | err = pnp_activate_dev(pdev); | ||
603 | if (err < 0) { | ||
604 | kfree(cfg); | ||
605 | snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n"); | ||
606 | return err; | ||
607 | } | ||
608 | if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) || | ||
609 | pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) { | ||
610 | kfree(cfg); | ||
611 | snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n"); | ||
612 | return -ENOENT; | ||
613 | } | ||
614 | port[dev] = pnp_port_start(pdev, 0); | ||
615 | dma1[dev] = pnp_dma(pdev, 0); | ||
616 | if (dma2[dev] >= 0) | ||
617 | dma2[dev] = pnp_dma(pdev, 1); | ||
618 | irq[dev] = pnp_irq(pdev, 0); | ||
619 | snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n", | ||
620 | pnp_port_start(pdev, 0), | ||
621 | pnp_port_start(pdev, 1), | ||
622 | pnp_port_start(pdev, 2)); | ||
623 | snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); | ||
624 | #ifdef SNDRV_STB | ||
625 | /* Tone Control initialization */ | ||
626 | pdev = iwcard->devtc; | ||
627 | pnp_init_resource_table(cfg); | ||
628 | if (port_tc[dev] != SNDRV_AUTO_PORT) | ||
629 | pnp_resource_change(&cfg->port_resource[0], port_tc[dev], 1); | ||
630 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
631 | snd_printk(KERN_ERR "InterWave - ToneControl - the requested resources are invalid, using auto config\n"); | ||
632 | err = pnp_activate_dev(pdev); | ||
633 | if (err < 0) { | ||
634 | kfree(cfg); | ||
635 | snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n"); | ||
636 | return err; | ||
637 | } | ||
638 | port_tc[dev] = pnp_port_start(pdev, 0); | ||
639 | snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]); | ||
640 | #endif | ||
641 | kfree(cfg); | ||
642 | return 0; | ||
643 | } | ||
644 | #endif /* CONFIG_PNP */ | ||
645 | |||
646 | static void snd_interwave_free(snd_card_t *card) | ||
647 | { | ||
648 | struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data; | ||
649 | |||
650 | if (iwcard == NULL) | ||
651 | return; | ||
652 | #ifdef SNDRV_STB | ||
653 | if (iwcard->i2c_res) { | ||
654 | release_resource(iwcard->i2c_res); | ||
655 | kfree_nocheck(iwcard->i2c_res); | ||
656 | } | ||
657 | #endif | ||
658 | if (iwcard->irq >= 0) | ||
659 | free_irq(iwcard->irq, (void *)iwcard); | ||
660 | } | ||
661 | |||
662 | static int __devinit snd_interwave_probe(int dev, struct pnp_card_link *pcard, | ||
663 | const struct pnp_card_device_id *pid) | ||
664 | { | ||
665 | static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; | ||
666 | static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1}; | ||
667 | int xirq, xdma1, xdma2; | ||
668 | snd_card_t *card; | ||
669 | struct snd_interwave *iwcard; | ||
670 | cs4231_t *cs4231; | ||
671 | snd_gus_card_t *gus; | ||
672 | #ifdef SNDRV_STB | ||
673 | snd_i2c_bus_t *i2c_bus; | ||
674 | #endif | ||
675 | snd_pcm_t *pcm; | ||
676 | char *str; | ||
677 | int err; | ||
678 | |||
679 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
680 | sizeof(struct snd_interwave)); | ||
681 | if (card == NULL) | ||
682 | return -ENOMEM; | ||
683 | iwcard = (struct snd_interwave *)card->private_data; | ||
684 | iwcard->card = card; | ||
685 | iwcard->irq = -1; | ||
686 | card->private_free = snd_interwave_free; | ||
687 | #ifdef CONFIG_PNP | ||
688 | if (isapnp[dev]) { | ||
689 | if (snd_interwave_pnp(dev, iwcard, pcard, pid)) { | ||
690 | snd_card_free(card); | ||
691 | return -ENODEV; | ||
692 | } | ||
693 | snd_card_set_dev(card, &pcard->card->dev); | ||
694 | } | ||
695 | #endif | ||
696 | xirq = irq[dev]; | ||
697 | if (xirq == SNDRV_AUTO_IRQ) { | ||
698 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
699 | snd_card_free(card); | ||
700 | snd_printk("unable to find a free IRQ\n"); | ||
701 | return -EBUSY; | ||
702 | } | ||
703 | } | ||
704 | xdma1 = dma1[dev]; | ||
705 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
706 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
707 | snd_card_free(card); | ||
708 | snd_printk("unable to find a free DMA1\n"); | ||
709 | return -EBUSY; | ||
710 | } | ||
711 | } | ||
712 | xdma2 = dma2[dev]; | ||
713 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
714 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
715 | snd_card_free(card); | ||
716 | snd_printk("unable to find a free DMA2\n"); | ||
717 | return -EBUSY; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | if ((err = snd_gus_create(card, | ||
722 | port[dev], | ||
723 | -xirq, xdma1, xdma2, | ||
724 | 0, 32, | ||
725 | pcm_channels[dev], effect[dev], &gus)) < 0) { | ||
726 | snd_card_free(card); | ||
727 | return err; | ||
728 | } | ||
729 | if ((err = snd_interwave_detect(iwcard, gus, dev | ||
730 | #ifdef SNDRV_STB | ||
731 | , &i2c_bus | ||
732 | #endif | ||
733 | )) < 0) { | ||
734 | snd_card_free(card); | ||
735 | return err; | ||
736 | } | ||
737 | iwcard->gus_status_reg = gus->gf1.reg_irqstat; | ||
738 | iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; | ||
739 | |||
740 | snd_interwave_init(dev, gus); | ||
741 | snd_interwave_detect_memory(gus); | ||
742 | if ((err = snd_gus_initialize(gus)) < 0) { | ||
743 | snd_card_free(card); | ||
744 | return err; | ||
745 | } | ||
746 | |||
747 | if (request_irq(xirq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) { | ||
748 | snd_card_free(card); | ||
749 | snd_printk("unable to grab IRQ %d\n", xirq); | ||
750 | return -EBUSY; | ||
751 | } | ||
752 | iwcard->irq = xirq; | ||
753 | |||
754 | if ((err = snd_cs4231_create(card, | ||
755 | gus->gf1.port + 0x10c, -1, xirq, | ||
756 | xdma2 < 0 ? xdma1 : xdma2, xdma1, | ||
757 | CS4231_HW_INTERWAVE, | ||
758 | CS4231_HWSHARE_IRQ | | ||
759 | CS4231_HWSHARE_DMA1 | | ||
760 | CS4231_HWSHARE_DMA2, | ||
761 | &cs4231)) < 0) { | ||
762 | snd_card_free(card); | ||
763 | return err; | ||
764 | } | ||
765 | if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) { | ||
766 | snd_card_free(card); | ||
767 | return err; | ||
768 | } | ||
769 | sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); | ||
770 | strcat(pcm->name, " (codec)"); | ||
771 | if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { | ||
772 | snd_card_free(card); | ||
773 | return err; | ||
774 | } | ||
775 | if ((err = snd_cs4231_mixer(cs4231)) < 0) { | ||
776 | snd_card_free(card); | ||
777 | return err; | ||
778 | } | ||
779 | if (pcm_channels[dev] > 0) { | ||
780 | if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { | ||
781 | snd_card_free(card); | ||
782 | return err; | ||
783 | } | ||
784 | } | ||
785 | if ((err = snd_interwave_mixer(cs4231)) < 0) { | ||
786 | snd_card_free(card); | ||
787 | return err; | ||
788 | } | ||
789 | #ifdef SNDRV_STB | ||
790 | { | ||
791 | snd_ctl_elem_id_t id1, id2; | ||
792 | memset(&id1, 0, sizeof(id1)); | ||
793 | memset(&id2, 0, sizeof(id2)); | ||
794 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
795 | strcpy(id1.name, "Master Playback Switch"); | ||
796 | strcpy(id2.name, id1.name); | ||
797 | id2.index = 1; | ||
798 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { | ||
799 | snd_card_free(card); | ||
800 | return err; | ||
801 | } | ||
802 | strcpy(id1.name, "Master Playback Volume"); | ||
803 | strcpy(id2.name, id1.name); | ||
804 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { | ||
805 | snd_card_free(card); | ||
806 | return err; | ||
807 | } | ||
808 | if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) { | ||
809 | snd_card_free(card); | ||
810 | return err; | ||
811 | } | ||
812 | } | ||
813 | #endif | ||
814 | |||
815 | gus->uart_enable = midi[dev]; | ||
816 | if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { | ||
817 | snd_card_free(card); | ||
818 | return err; | ||
819 | } | ||
820 | |||
821 | #ifndef SNDRV_STB | ||
822 | str = "AMD InterWave"; | ||
823 | if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) | ||
824 | str = "Dynasonic 3-D"; | ||
825 | #else | ||
826 | str = "InterWave STB"; | ||
827 | #endif | ||
828 | strcpy(card->driver, str); | ||
829 | strcpy(card->shortname, str); | ||
830 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", | ||
831 | str, | ||
832 | gus->gf1.port, | ||
833 | xirq, | ||
834 | xdma1); | ||
835 | if (xdma2 >= 0) | ||
836 | sprintf(card->longname + strlen(card->longname), "&%d", xdma2); | ||
837 | |||
838 | if ((err = snd_card_register(card)) < 0) { | ||
839 | snd_card_free(card); | ||
840 | return err; | ||
841 | } | ||
842 | |||
843 | iwcard->cs4231 = cs4231; | ||
844 | iwcard->gus = gus; | ||
845 | if (pcard) | ||
846 | pnp_set_card_drvdata(pcard, card); | ||
847 | else | ||
848 | snd_interwave_legacy[dev++] = card; | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static int __devinit snd_interwave_probe_legacy_port(unsigned long xport) | ||
853 | { | ||
854 | static int dev; | ||
855 | int res; | ||
856 | |||
857 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
858 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
859 | continue; | ||
860 | #ifdef CONFIG_PNP | ||
861 | if (isapnp[dev]) | ||
862 | continue; | ||
863 | #endif | ||
864 | port[dev] = xport; | ||
865 | res = snd_interwave_probe(dev, NULL, NULL); | ||
866 | if (res < 0) | ||
867 | port[dev] = SNDRV_AUTO_PORT; | ||
868 | return res; | ||
869 | } | ||
870 | return -ENODEV; | ||
871 | } | ||
872 | |||
873 | #ifdef CONFIG_PNP | ||
874 | |||
875 | static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *card, | ||
876 | const struct pnp_card_device_id *id) | ||
877 | { | ||
878 | static int dev; | ||
879 | int res; | ||
880 | |||
881 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
882 | if (!enable[dev] || !isapnp[dev]) | ||
883 | continue; | ||
884 | res = snd_interwave_probe(dev, card, id); | ||
885 | if (res < 0) | ||
886 | return res; | ||
887 | dev++; | ||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | return -ENODEV; | ||
892 | } | ||
893 | |||
894 | static void __devexit snd_interwave_pnp_remove(struct pnp_card_link * pcard) | ||
895 | { | ||
896 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
897 | |||
898 | snd_card_disconnect(card); | ||
899 | snd_card_free_in_thread(card); | ||
900 | } | ||
901 | |||
902 | static struct pnp_card_driver interwave_pnpc_driver = { | ||
903 | .flags = PNP_DRIVER_RES_DISABLE, | ||
904 | .name = "interwave", | ||
905 | .id_table = snd_interwave_pnpids, | ||
906 | .probe = snd_interwave_pnp_detect, | ||
907 | .remove = __devexit_p(snd_interwave_pnp_remove), | ||
908 | }; | ||
909 | |||
910 | #endif /* CONFIG_PNP */ | ||
911 | |||
912 | static int __init alsa_card_interwave_init(void) | ||
913 | { | ||
914 | int cards = 0, i; | ||
915 | static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1}; | ||
916 | int dev; | ||
917 | |||
918 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
919 | if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) | ||
920 | continue; | ||
921 | #ifdef CONFIG_PNP | ||
922 | if (isapnp[dev]) | ||
923 | continue; | ||
924 | #endif | ||
925 | if (!snd_interwave_probe(dev, NULL, NULL)) { | ||
926 | cards++; | ||
927 | continue; | ||
928 | } | ||
929 | #ifdef MODULE | ||
930 | printk(KERN_ERR "InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, port[dev]); | ||
931 | #endif | ||
932 | } | ||
933 | /* legacy auto configured cards */ | ||
934 | i = snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port); | ||
935 | if (i > 0) | ||
936 | cards += i; | ||
937 | #ifdef CONFIG_PNP | ||
938 | /* ISA PnP cards */ | ||
939 | i = pnp_register_card_driver(&interwave_pnpc_driver); | ||
940 | if (i > 0) | ||
941 | cards += i; | ||
942 | #endif | ||
943 | |||
944 | if (!cards) { | ||
945 | #ifdef CONFIG_PNP | ||
946 | pnp_unregister_card_driver(&interwave_pnpc_driver); | ||
947 | #endif | ||
948 | #ifdef MODULE | ||
949 | printk(KERN_ERR "InterWave soundcard not found or device busy\n"); | ||
950 | #endif | ||
951 | return -ENODEV; | ||
952 | } | ||
953 | return 0; | ||
954 | } | ||
955 | |||
956 | static void __exit alsa_card_interwave_exit(void) | ||
957 | { | ||
958 | int dev; | ||
959 | |||
960 | #ifdef CONFIG_PNP | ||
961 | /* PnP cards first */ | ||
962 | pnp_unregister_card_driver(&interwave_pnpc_driver); | ||
963 | #endif | ||
964 | for (dev = 0; dev < SNDRV_CARDS; dev++) | ||
965 | snd_card_free(snd_interwave_legacy[dev]); | ||
966 | } | ||
967 | |||
968 | module_init(alsa_card_interwave_init) | ||
969 | module_exit(alsa_card_interwave_exit) | ||
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c new file mode 100644 index 000000000000..95c7b3e53407 --- /dev/null +++ b/sound/isa/opl3sa2.c | |||
@@ -0,0 +1,860 @@ | |||
1 | /* | ||
2 | * Driver for Yamaha OPL3-SA[2,3] soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/pm.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/pnp.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/cs4231.h> | ||
31 | #include <sound/mpu401.h> | ||
32 | #include <sound/opl3.h> | ||
33 | #include <sound/initval.h> | ||
34 | |||
35 | #include <asm/io.h> | ||
36 | |||
37 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
38 | MODULE_DESCRIPTION("Yamaha OPL3SA2+"); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF719E-S}," | ||
41 | "{Genius,Sound Maker 3DX}," | ||
42 | "{Yamaha,OPL3SA3}," | ||
43 | "{Intel,AL440LX sound}," | ||
44 | "{NeoMagic,MagicWave 3DX}}"); | ||
45 | |||
46 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
47 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
48 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
49 | #ifdef CONFIG_PNP | ||
50 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
51 | #endif | ||
52 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0xf86,0x370,0x100 */ | ||
53 | static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ | ||
54 | static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */ | ||
55 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x388 */ | ||
56 | static long midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */ | ||
57 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 0,1,3,5,9,11,12,15 */ | ||
58 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
59 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
60 | static int opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* 0,1,2,3 */ /*SL Added*/ | ||
61 | |||
62 | module_param_array(index, int, NULL, 0444); | ||
63 | MODULE_PARM_DESC(index, "Index value for OPL3-SA soundcard."); | ||
64 | module_param_array(id, charp, NULL, 0444); | ||
65 | MODULE_PARM_DESC(id, "ID string for OPL3-SA soundcard."); | ||
66 | module_param_array(enable, bool, NULL, 0444); | ||
67 | MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard."); | ||
68 | #ifdef CONFIG_PNP | ||
69 | module_param_array(isapnp, bool, NULL, 0444); | ||
70 | MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); | ||
71 | #endif | ||
72 | module_param_array(port, long, NULL, 0444); | ||
73 | MODULE_PARM_DESC(port, "Port # for OPL3-SA driver."); | ||
74 | module_param_array(sb_port, long, NULL, 0444); | ||
75 | MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver."); | ||
76 | module_param_array(wss_port, long, NULL, 0444); | ||
77 | MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver."); | ||
78 | module_param_array(fm_port, long, NULL, 0444); | ||
79 | MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver."); | ||
80 | module_param_array(midi_port, long, NULL, 0444); | ||
81 | MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver."); | ||
82 | module_param_array(irq, int, NULL, 0444); | ||
83 | MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver."); | ||
84 | module_param_array(dma1, int, NULL, 0444); | ||
85 | MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver."); | ||
86 | module_param_array(dma2, int, NULL, 0444); | ||
87 | MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver."); | ||
88 | module_param_array(opl3sa3_ymode, int, NULL, 0444); | ||
89 | MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi."); | ||
90 | |||
91 | /* control ports */ | ||
92 | #define OPL3SA2_PM_CTRL 0x01 | ||
93 | #define OPL3SA2_SYS_CTRL 0x02 | ||
94 | #define OPL3SA2_IRQ_CONFIG 0x03 | ||
95 | #define OPL3SA2_IRQ_STATUS 0x04 | ||
96 | #define OPL3SA2_DMA_CONFIG 0x06 | ||
97 | #define OPL3SA2_MASTER_LEFT 0x07 | ||
98 | #define OPL3SA2_MASTER_RIGHT 0x08 | ||
99 | #define OPL3SA2_MIC 0x09 | ||
100 | #define OPL3SA2_MISC 0x0A | ||
101 | |||
102 | /* opl3sa3 only */ | ||
103 | #define OPL3SA3_DGTL_DOWN 0x12 | ||
104 | #define OPL3SA3_ANLG_DOWN 0x13 | ||
105 | #define OPL3SA3_WIDE 0x14 | ||
106 | #define OPL3SA3_BASS 0x15 | ||
107 | #define OPL3SA3_TREBLE 0x16 | ||
108 | |||
109 | /* power management bits */ | ||
110 | #define OPL3SA2_PM_ADOWN 0x20 | ||
111 | #define OPL3SA2_PM_PSV 0x04 | ||
112 | #define OPL3SA2_PM_PDN 0x02 | ||
113 | #define OPL3SA2_PM_PDX 0x01 | ||
114 | |||
115 | #define OPL3SA2_PM_D0 0x00 | ||
116 | #define OPL3SA2_PM_D3 (OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX) | ||
117 | |||
118 | typedef struct snd_opl3sa2 opl3sa2_t; | ||
119 | |||
120 | struct snd_opl3sa2 { | ||
121 | snd_card_t *card; | ||
122 | int version; /* 2 or 3 */ | ||
123 | unsigned long port; /* control port */ | ||
124 | struct resource *res_port; /* control port resource */ | ||
125 | int irq; | ||
126 | int single_dma; | ||
127 | spinlock_t reg_lock; | ||
128 | snd_hwdep_t *synth; | ||
129 | snd_rawmidi_t *rmidi; | ||
130 | cs4231_t *cs4231; | ||
131 | #ifdef CONFIG_PNP | ||
132 | struct pnp_dev *dev; | ||
133 | #endif | ||
134 | unsigned char ctlregs[0x20]; | ||
135 | int ymode; /* SL added */ | ||
136 | snd_kcontrol_t *master_switch; | ||
137 | snd_kcontrol_t *master_volume; | ||
138 | #ifdef CONFIG_PM | ||
139 | void (*cs4231_suspend)(cs4231_t *); | ||
140 | void (*cs4231_resume)(cs4231_t *); | ||
141 | #endif | ||
142 | }; | ||
143 | |||
144 | static snd_card_t *snd_opl3sa2_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
145 | |||
146 | #ifdef CONFIG_PNP | ||
147 | |||
148 | static struct pnp_card_device_id snd_opl3sa2_pnpids[] = { | ||
149 | /* Yamaha YMF719E-S (Genius Sound Maker 3DX) */ | ||
150 | { .id = "YMH0020", .devs = { { "YMH0021" } } }, | ||
151 | /* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */ | ||
152 | { .id = "YMH0030", .devs = { { "YMH0021" } } }, | ||
153 | /* Yamaha OPL3-SA2 */ | ||
154 | { .id = "YMH0800", .devs = { { "YMH0021" } } }, | ||
155 | /* Yamaha OPL3-SA2 */ | ||
156 | { .id = "YMH0801", .devs = { { "YMH0021" } } }, | ||
157 | /* NeoMagic MagicWave 3DX */ | ||
158 | { .id = "NMX2200", .devs = { { "YMH2210" } } }, | ||
159 | /* --- */ | ||
160 | { .id = "" } /* end */ | ||
161 | }; | ||
162 | |||
163 | MODULE_DEVICE_TABLE(pnp_card, snd_opl3sa2_pnpids); | ||
164 | |||
165 | #endif /* CONFIG_PNP */ | ||
166 | |||
167 | |||
168 | /* read control port (w/o spinlock) */ | ||
169 | static unsigned char __snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) | ||
170 | { | ||
171 | unsigned char result; | ||
172 | #if 0 | ||
173 | outb(0x1d, port); /* password */ | ||
174 | printk("read [0x%lx] = 0x%x\n", port, inb(port)); | ||
175 | #endif | ||
176 | outb(reg, chip->port); /* register */ | ||
177 | result = inb(chip->port + 1); | ||
178 | #if 0 | ||
179 | printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port)); | ||
180 | #endif | ||
181 | return result; | ||
182 | } | ||
183 | |||
184 | /* read control port (with spinlock) */ | ||
185 | static unsigned char snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) | ||
186 | { | ||
187 | unsigned long flags; | ||
188 | unsigned char result; | ||
189 | |||
190 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
191 | result = __snd_opl3sa2_read(chip, reg); | ||
192 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
193 | return result; | ||
194 | } | ||
195 | |||
196 | /* write control port (w/o spinlock) */ | ||
197 | static void __snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) | ||
198 | { | ||
199 | #if 0 | ||
200 | outb(0x1d, port); /* password */ | ||
201 | #endif | ||
202 | outb(reg, chip->port); /* register */ | ||
203 | outb(value, chip->port + 1); | ||
204 | chip->ctlregs[reg] = value; | ||
205 | } | ||
206 | |||
207 | /* write control port (with spinlock) */ | ||
208 | static void snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) | ||
209 | { | ||
210 | unsigned long flags; | ||
211 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
212 | __snd_opl3sa2_write(chip, reg, value); | ||
213 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
214 | } | ||
215 | |||
216 | static int __init snd_opl3sa2_detect(opl3sa2_t *chip) | ||
217 | { | ||
218 | snd_card_t *card; | ||
219 | unsigned long port; | ||
220 | unsigned char tmp, tmp1; | ||
221 | char str[2]; | ||
222 | |||
223 | card = chip->card; | ||
224 | port = chip->port; | ||
225 | if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) { | ||
226 | snd_printk(KERN_ERR "opl3sa2: can't grab port 0x%lx\n", port); | ||
227 | return -EBUSY; | ||
228 | } | ||
229 | // snd_printk("REG 0A = 0x%x\n", snd_opl3sa2_read(chip, 0x0a)); | ||
230 | chip->version = 0; | ||
231 | tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC); | ||
232 | if (tmp == 0xff) { | ||
233 | snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp); | ||
234 | return -ENODEV; | ||
235 | } | ||
236 | switch (tmp & 0x07) { | ||
237 | case 0x01: | ||
238 | chip->version = 2; /* YMF711 */ | ||
239 | break; | ||
240 | default: | ||
241 | chip->version = 3; | ||
242 | /* 0x02 - standard */ | ||
243 | /* 0x03 - YM715B */ | ||
244 | /* 0x04 - YM719 - OPL-SA4? */ | ||
245 | /* 0x05 - OPL3-SA3 - Libretto 100 */ | ||
246 | break; | ||
247 | } | ||
248 | str[0] = chip->version + '0'; | ||
249 | str[1] = 0; | ||
250 | strcat(card->shortname, str); | ||
251 | snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7); | ||
252 | if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) { | ||
253 | snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1); | ||
254 | return -ENODEV; | ||
255 | } | ||
256 | /* try if the MIC register is accesible */ | ||
257 | tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC); | ||
258 | snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a); | ||
259 | if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) { | ||
260 | snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1); | ||
261 | return -ENODEV; | ||
262 | } | ||
263 | snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f); | ||
264 | /* initialization */ | ||
265 | /* Power Management - full on */ | ||
266 | snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); | ||
267 | if (chip->version > 2) { | ||
268 | /* ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */ | ||
269 | snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, (chip->ymode << 4)); | ||
270 | } else { | ||
271 | /* default for opl3sa2 versions */ | ||
272 | snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, 0x00); | ||
273 | } | ||
274 | snd_opl3sa2_write(chip, OPL3SA2_IRQ_CONFIG, 0x0d); /* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */ | ||
275 | if (chip->single_dma) { | ||
276 | snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x03); /* DMA Configuration - DMA A = WSS-R + WSS-P */ | ||
277 | } else { | ||
278 | snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x21); /* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */ | ||
279 | } | ||
280 | snd_opl3sa2_write(chip, OPL3SA2_MISC, 0x80 | (tmp & 7)); /* Miscellaneous - default */ | ||
281 | if (chip->version > 2) { | ||
282 | snd_opl3sa2_write(chip, OPL3SA3_DGTL_DOWN, 0x00); /* Digital Block Partial Power Down - default */ | ||
283 | snd_opl3sa2_write(chip, OPL3SA3_ANLG_DOWN, 0x00); /* Analog Block Partial Power Down - default */ | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
289 | { | ||
290 | unsigned short status; | ||
291 | opl3sa2_t *chip = dev_id; | ||
292 | int handled = 0; | ||
293 | |||
294 | if (chip == NULL || chip->card == NULL) | ||
295 | return IRQ_NONE; | ||
296 | |||
297 | status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS); | ||
298 | |||
299 | if (status & 0x20) { | ||
300 | handled = 1; | ||
301 | snd_opl3_interrupt(chip->synth); | ||
302 | } | ||
303 | |||
304 | if ((status & 0x10) && chip->rmidi != NULL) { | ||
305 | handled = 1; | ||
306 | snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); | ||
307 | } | ||
308 | |||
309 | if (status & 0x07) { /* TI,CI,PI */ | ||
310 | handled = 1; | ||
311 | snd_cs4231_interrupt(irq, chip->cs4231, regs); | ||
312 | } | ||
313 | |||
314 | if (status & 0x40) { /* hardware volume change */ | ||
315 | handled = 1; | ||
316 | /* reading from Master Lch register at 0x07 clears this bit */ | ||
317 | snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT); | ||
318 | snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT); | ||
319 | if (chip->master_switch && chip->master_volume) { | ||
320 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); | ||
321 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); | ||
322 | } | ||
323 | } | ||
324 | return IRQ_RETVAL(handled); | ||
325 | } | ||
326 | |||
327 | #define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
328 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
329 | .info = snd_opl3sa2_info_single, \ | ||
330 | .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ | ||
331 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
332 | |||
333 | static int snd_opl3sa2_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
334 | { | ||
335 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
336 | |||
337 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
338 | uinfo->count = 1; | ||
339 | uinfo->value.integer.min = 0; | ||
340 | uinfo->value.integer.max = mask; | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int snd_opl3sa2_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
345 | { | ||
346 | opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); | ||
347 | unsigned long flags; | ||
348 | int reg = kcontrol->private_value & 0xff; | ||
349 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
350 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
351 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
352 | |||
353 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
354 | ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask; | ||
355 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
356 | if (invert) | ||
357 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static int snd_opl3sa2_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
362 | { | ||
363 | opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); | ||
364 | unsigned long flags; | ||
365 | int reg = kcontrol->private_value & 0xff; | ||
366 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
367 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
368 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
369 | int change; | ||
370 | unsigned short val, oval; | ||
371 | |||
372 | val = (ucontrol->value.integer.value[0] & mask); | ||
373 | if (invert) | ||
374 | val = mask - val; | ||
375 | val <<= shift; | ||
376 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
377 | oval = chip->ctlregs[reg]; | ||
378 | val = (oval & ~(mask << shift)) | val; | ||
379 | change = val != oval; | ||
380 | __snd_opl3sa2_write(chip, reg, val); | ||
381 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
382 | return change; | ||
383 | } | ||
384 | |||
385 | #define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
386 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
387 | .info = snd_opl3sa2_info_double, \ | ||
388 | .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ | ||
389 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
390 | |||
391 | static int snd_opl3sa2_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
392 | { | ||
393 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
394 | |||
395 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
396 | uinfo->count = 2; | ||
397 | uinfo->value.integer.min = 0; | ||
398 | uinfo->value.integer.max = mask; | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int snd_opl3sa2_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
403 | { | ||
404 | opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); | ||
405 | unsigned long flags; | ||
406 | int left_reg = kcontrol->private_value & 0xff; | ||
407 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
408 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
409 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
410 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
411 | int invert = (kcontrol->private_value >> 22) & 1; | ||
412 | |||
413 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
414 | ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask; | ||
415 | ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask; | ||
416 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
417 | if (invert) { | ||
418 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
419 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
420 | } | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static int snd_opl3sa2_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
425 | { | ||
426 | opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); | ||
427 | unsigned long flags; | ||
428 | int left_reg = kcontrol->private_value & 0xff; | ||
429 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
430 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
431 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
432 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
433 | int invert = (kcontrol->private_value >> 22) & 1; | ||
434 | int change; | ||
435 | unsigned short val1, val2, oval1, oval2; | ||
436 | |||
437 | val1 = ucontrol->value.integer.value[0] & mask; | ||
438 | val2 = ucontrol->value.integer.value[1] & mask; | ||
439 | if (invert) { | ||
440 | val1 = mask - val1; | ||
441 | val2 = mask - val2; | ||
442 | } | ||
443 | val1 <<= shift_left; | ||
444 | val2 <<= shift_right; | ||
445 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
446 | if (left_reg != right_reg) { | ||
447 | oval1 = chip->ctlregs[left_reg]; | ||
448 | oval2 = chip->ctlregs[right_reg]; | ||
449 | val1 = (oval1 & ~(mask << shift_left)) | val1; | ||
450 | val2 = (oval2 & ~(mask << shift_right)) | val2; | ||
451 | change = val1 != oval1 || val2 != oval2; | ||
452 | __snd_opl3sa2_write(chip, left_reg, val1); | ||
453 | __snd_opl3sa2_write(chip, right_reg, val2); | ||
454 | } else { | ||
455 | oval1 = chip->ctlregs[left_reg]; | ||
456 | val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
457 | change = val1 != oval1; | ||
458 | __snd_opl3sa2_write(chip, left_reg, val1); | ||
459 | } | ||
460 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
461 | return change; | ||
462 | } | ||
463 | |||
464 | static snd_kcontrol_new_t snd_opl3sa2_controls[] = { | ||
465 | OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), | ||
466 | OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), | ||
467 | OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1), | ||
468 | OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) | ||
469 | }; | ||
470 | |||
471 | static snd_kcontrol_new_t snd_opl3sa2_tone_controls[] = { | ||
472 | OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0), | ||
473 | OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0), | ||
474 | OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0) | ||
475 | }; | ||
476 | |||
477 | static void snd_opl3sa2_master_free(snd_kcontrol_t *kcontrol) | ||
478 | { | ||
479 | opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); | ||
480 | chip->master_switch = NULL; | ||
481 | chip->master_volume = NULL; | ||
482 | } | ||
483 | |||
484 | static int __init snd_opl3sa2_mixer(opl3sa2_t *chip) | ||
485 | { | ||
486 | snd_card_t *card = chip->card; | ||
487 | snd_ctl_elem_id_t id1, id2; | ||
488 | snd_kcontrol_t *kctl; | ||
489 | unsigned int idx; | ||
490 | int err; | ||
491 | |||
492 | memset(&id1, 0, sizeof(id1)); | ||
493 | memset(&id2, 0, sizeof(id2)); | ||
494 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
495 | /* reassign AUX0 to CD */ | ||
496 | strcpy(id1.name, "Aux Playback Switch"); | ||
497 | strcpy(id2.name, "CD Playback Switch"); | ||
498 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
499 | return err; | ||
500 | strcpy(id1.name, "Aux Playback Volume"); | ||
501 | strcpy(id2.name, "CD Playback Volume"); | ||
502 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
503 | return err; | ||
504 | /* reassign AUX1 to FM */ | ||
505 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
506 | strcpy(id2.name, "FM Playback Switch"); | ||
507 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
508 | return err; | ||
509 | strcpy(id1.name, "Aux Playback Volume"); | ||
510 | strcpy(id2.name, "FM Playback Volume"); | ||
511 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
512 | return err; | ||
513 | /* add OPL3SA2 controls */ | ||
514 | for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_controls); idx++) { | ||
515 | if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0) | ||
516 | return err; | ||
517 | switch (idx) { | ||
518 | case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break; | ||
519 | case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break; | ||
520 | } | ||
521 | } | ||
522 | if (chip->version > 2) { | ||
523 | for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++) | ||
524 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0) | ||
525 | return err; | ||
526 | } | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | /* Power Management support functions */ | ||
531 | #ifdef CONFIG_PM | ||
532 | static int snd_opl3sa2_suspend(snd_card_t *card, pm_message_t state) | ||
533 | { | ||
534 | opl3sa2_t *chip = card->pm_private_data; | ||
535 | |||
536 | snd_pcm_suspend_all(chip->cs4231->pcm); /* stop before saving regs */ | ||
537 | chip->cs4231_suspend(chip->cs4231); | ||
538 | |||
539 | /* power down */ | ||
540 | snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3); | ||
541 | |||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static int snd_opl3sa2_resume(snd_card_t *card) | ||
546 | { | ||
547 | opl3sa2_t *chip = card->pm_private_data; | ||
548 | int i; | ||
549 | |||
550 | /* power up */ | ||
551 | snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); | ||
552 | |||
553 | /* restore registers */ | ||
554 | for (i = 2; i <= 0x0a; i++) { | ||
555 | if (i != OPL3SA2_IRQ_STATUS) | ||
556 | snd_opl3sa2_write(chip, i, chip->ctlregs[i]); | ||
557 | } | ||
558 | if (chip->version > 2) { | ||
559 | for (i = 0x12; i <= 0x16; i++) | ||
560 | snd_opl3sa2_write(chip, i, chip->ctlregs[i]); | ||
561 | } | ||
562 | /* restore cs4231 */ | ||
563 | chip->cs4231_resume(chip->cs4231); | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | #endif /* CONFIG_PM */ | ||
568 | |||
569 | #ifdef CONFIG_PNP | ||
570 | static int __init snd_opl3sa2_pnp(int dev, opl3sa2_t *chip, | ||
571 | struct pnp_card_link *card, | ||
572 | const struct pnp_card_device_id *id) | ||
573 | { | ||
574 | struct pnp_dev *pdev; | ||
575 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
576 | int err; | ||
577 | |||
578 | if (!cfg) | ||
579 | return -ENOMEM; | ||
580 | pdev = chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
581 | if (chip->dev == NULL) { | ||
582 | kfree(cfg); | ||
583 | return -EBUSY; | ||
584 | } | ||
585 | /* PnP initialization */ | ||
586 | pnp_init_resource_table(cfg); | ||
587 | if (sb_port[dev] != SNDRV_AUTO_PORT) | ||
588 | pnp_resource_change(&cfg->port_resource[0], sb_port[dev], 16); | ||
589 | if (wss_port[dev] != SNDRV_AUTO_PORT) | ||
590 | pnp_resource_change(&cfg->port_resource[1], wss_port[dev], 8); | ||
591 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
592 | pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4); | ||
593 | if (midi_port[dev] != SNDRV_AUTO_PORT) | ||
594 | pnp_resource_change(&cfg->port_resource[3], midi_port[dev], 2); | ||
595 | if (port[dev] != SNDRV_AUTO_PORT) | ||
596 | pnp_resource_change(&cfg->port_resource[4], port[dev], 2); | ||
597 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
598 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
599 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
600 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
601 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
602 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
603 | err = pnp_manual_config_dev(pdev, cfg, 0); | ||
604 | if (err < 0) | ||
605 | snd_printk(KERN_ERR "PnP manual resources are invalid, using auto config\n"); | ||
606 | err = pnp_activate_dev(pdev); | ||
607 | if (err < 0) { | ||
608 | kfree(cfg); | ||
609 | snd_printk(KERN_ERR "PnP configure failure (out of resources?) err = %d\n", err); | ||
610 | return -EBUSY; | ||
611 | } | ||
612 | sb_port[dev] = pnp_port_start(pdev, 0); | ||
613 | wss_port[dev] = pnp_port_start(pdev, 1); | ||
614 | fm_port[dev] = pnp_port_start(pdev, 2); | ||
615 | midi_port[dev] = pnp_port_start(pdev, 3); | ||
616 | port[dev] = pnp_port_start(pdev, 4); | ||
617 | dma1[dev] = pnp_dma(pdev, 0); | ||
618 | dma2[dev] = pnp_dma(pdev, 1); | ||
619 | irq[dev] = pnp_irq(pdev, 0); | ||
620 | snd_printdd("PnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", | ||
621 | sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]); | ||
622 | snd_printdd("PnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", | ||
623 | port[dev], dma1[dev], dma2[dev], irq[dev]); | ||
624 | kfree(cfg); | ||
625 | return 0; | ||
626 | } | ||
627 | #endif /* CONFIG_PNP */ | ||
628 | |||
629 | static int snd_opl3sa2_free(opl3sa2_t *chip) | ||
630 | { | ||
631 | if (chip->irq >= 0) | ||
632 | free_irq(chip->irq, (void *)chip); | ||
633 | if (chip->res_port) { | ||
634 | release_resource(chip->res_port); | ||
635 | kfree_nocheck(chip->res_port); | ||
636 | } | ||
637 | kfree(chip); | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | static int snd_opl3sa2_dev_free(snd_device_t *device) | ||
642 | { | ||
643 | opl3sa2_t *chip = device->device_data; | ||
644 | return snd_opl3sa2_free(chip); | ||
645 | } | ||
646 | |||
647 | static int __devinit snd_opl3sa2_probe(int dev, | ||
648 | struct pnp_card_link *pcard, | ||
649 | const struct pnp_card_device_id *pid) | ||
650 | { | ||
651 | int xirq, xdma1, xdma2; | ||
652 | snd_card_t *card; | ||
653 | struct snd_opl3sa2 *chip; | ||
654 | cs4231_t *cs4231; | ||
655 | opl3_t *opl3; | ||
656 | static snd_device_ops_t ops = { | ||
657 | .dev_free = snd_opl3sa2_dev_free, | ||
658 | }; | ||
659 | int err; | ||
660 | |||
661 | #ifdef CONFIG_PNP | ||
662 | if (!isapnp[dev]) { | ||
663 | #endif | ||
664 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
665 | snd_printk("specify port\n"); | ||
666 | return -EINVAL; | ||
667 | } | ||
668 | if (wss_port[dev] == SNDRV_AUTO_PORT) { | ||
669 | snd_printk("specify wss_port\n"); | ||
670 | return -EINVAL; | ||
671 | } | ||
672 | if (fm_port[dev] == SNDRV_AUTO_PORT) { | ||
673 | snd_printk("specify fm_port\n"); | ||
674 | return -EINVAL; | ||
675 | } | ||
676 | if (midi_port[dev] == SNDRV_AUTO_PORT) { | ||
677 | snd_printk("specify midi_port\n"); | ||
678 | return -EINVAL; | ||
679 | } | ||
680 | #ifdef CONFIG_PNP | ||
681 | } | ||
682 | #endif | ||
683 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
684 | if (card == NULL) | ||
685 | return -ENOMEM; | ||
686 | strcpy(card->driver, "OPL3SA2"); | ||
687 | strcpy(card->shortname, "Yamaha OPL3-SA2"); | ||
688 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
689 | if (chip == NULL) { | ||
690 | err = -ENOMEM; | ||
691 | goto __error; | ||
692 | } | ||
693 | spin_lock_init(&chip->reg_lock); | ||
694 | chip->irq = -1; | ||
695 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) | ||
696 | goto __error; | ||
697 | #ifdef CONFIG_PNP | ||
698 | if (isapnp[dev]) { | ||
699 | if ((err = snd_opl3sa2_pnp(dev, chip, pcard, pid)) < 0) | ||
700 | goto __error; | ||
701 | snd_card_set_dev(card, &pcard->card->dev); | ||
702 | } | ||
703 | #endif | ||
704 | chip->ymode = opl3sa3_ymode[dev] & 0x03 ; /* initialise this card from supplied (or default) parameter*/ | ||
705 | chip->card = card; | ||
706 | chip->port = port[dev]; | ||
707 | xirq = irq[dev]; | ||
708 | xdma1 = dma1[dev]; | ||
709 | xdma2 = dma2[dev]; | ||
710 | if (xdma2 < 0) | ||
711 | chip->single_dma = 1; | ||
712 | if ((err = snd_opl3sa2_detect(chip)) < 0) | ||
713 | goto __error; | ||
714 | if (request_irq(xirq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2", (void *)chip)) { | ||
715 | snd_printk(KERN_ERR "opl3sa2: can't grab IRQ %d\n", xirq); | ||
716 | err = -ENODEV; | ||
717 | goto __error; | ||
718 | } | ||
719 | chip->irq = xirq; | ||
720 | if ((err = snd_cs4231_create(card, | ||
721 | wss_port[dev] + 4, -1, | ||
722 | xirq, xdma1, xdma2, | ||
723 | CS4231_HW_OPL3SA2, | ||
724 | CS4231_HWSHARE_IRQ, | ||
725 | &cs4231)) < 0) { | ||
726 | snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4); | ||
727 | goto __error; | ||
728 | } | ||
729 | chip->cs4231 = cs4231; | ||
730 | if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) | ||
731 | goto __error; | ||
732 | if ((err = snd_cs4231_mixer(cs4231)) < 0) | ||
733 | goto __error; | ||
734 | if ((err = snd_opl3sa2_mixer(chip)) < 0) | ||
735 | goto __error; | ||
736 | if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0) | ||
737 | goto __error; | ||
738 | if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) { | ||
739 | if ((err = snd_opl3_create(card, fm_port[dev], | ||
740 | fm_port[dev] + 2, | ||
741 | OPL3_HW_OPL3, 0, &opl3)) < 0) | ||
742 | goto __error; | ||
743 | if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0) | ||
744 | goto __error; | ||
745 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0) | ||
746 | goto __error; | ||
747 | } | ||
748 | if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x340) { | ||
749 | if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2, | ||
750 | midi_port[dev], 0, | ||
751 | xirq, 0, &chip->rmidi)) < 0) | ||
752 | goto __error; | ||
753 | } | ||
754 | #ifdef CONFIG_PM | ||
755 | chip->cs4231_suspend = chip->cs4231->suspend; | ||
756 | chip->cs4231_resume = chip->cs4231->resume; | ||
757 | /* now clear callbacks for cs4231 */ | ||
758 | chip->cs4231->suspend = NULL; | ||
759 | chip->cs4231->resume = NULL; | ||
760 | snd_card_set_isa_pm_callback(card, snd_opl3sa2_suspend, snd_opl3sa2_resume, chip); | ||
761 | #endif | ||
762 | |||
763 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||
764 | card->shortname, chip->port, xirq, xdma1); | ||
765 | if (dma2 >= 0) | ||
766 | sprintf(card->longname + strlen(card->longname), "&%d", xdma2); | ||
767 | |||
768 | if ((err = snd_card_register(card)) < 0) | ||
769 | goto __error; | ||
770 | |||
771 | if (pcard) | ||
772 | pnp_set_card_drvdata(pcard, card); | ||
773 | else | ||
774 | snd_opl3sa2_legacy[dev] = card; | ||
775 | return 0; | ||
776 | |||
777 | __error: | ||
778 | snd_card_free(card); | ||
779 | return err; | ||
780 | } | ||
781 | |||
782 | #ifdef CONFIG_PNP | ||
783 | static int __devinit snd_opl3sa2_pnp_detect(struct pnp_card_link *card, | ||
784 | const struct pnp_card_device_id *id) | ||
785 | { | ||
786 | static int dev; | ||
787 | int res; | ||
788 | |||
789 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
790 | if (!enable[dev] || !isapnp[dev]) | ||
791 | continue; | ||
792 | res = snd_opl3sa2_probe(dev, card, id); | ||
793 | if (res < 0) | ||
794 | return res; | ||
795 | dev++; | ||
796 | return 0; | ||
797 | } | ||
798 | return -ENODEV; | ||
799 | } | ||
800 | |||
801 | static void __devexit snd_opl3sa2_pnp_remove(struct pnp_card_link * pcard) | ||
802 | { | ||
803 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
804 | |||
805 | snd_card_disconnect(card); | ||
806 | snd_card_free_in_thread(card); | ||
807 | } | ||
808 | |||
809 | static struct pnp_card_driver opl3sa2_pnpc_driver = { | ||
810 | .flags = PNP_DRIVER_RES_DISABLE, | ||
811 | .name = "opl3sa2", | ||
812 | .id_table = snd_opl3sa2_pnpids, | ||
813 | .probe = snd_opl3sa2_pnp_detect, | ||
814 | .remove = __devexit_p(snd_opl3sa2_pnp_remove), | ||
815 | }; | ||
816 | #endif /* CONFIG_PNP */ | ||
817 | |||
818 | static int __init alsa_card_opl3sa2_init(void) | ||
819 | { | ||
820 | int dev, cards = 0; | ||
821 | |||
822 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
823 | if (!enable[dev]) | ||
824 | continue; | ||
825 | #ifdef CONFIG_PNP | ||
826 | if (isapnp[dev]) | ||
827 | continue; | ||
828 | #endif | ||
829 | if (snd_opl3sa2_probe(dev, NULL, NULL) >= 0) | ||
830 | cards++; | ||
831 | } | ||
832 | #ifdef CONFIG_PNP | ||
833 | cards += pnp_register_card_driver(&opl3sa2_pnpc_driver); | ||
834 | #endif | ||
835 | if (!cards) { | ||
836 | #ifdef MODULE | ||
837 | snd_printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n"); | ||
838 | #endif | ||
839 | #ifdef CONFIG_PNP | ||
840 | pnp_unregister_card_driver(&opl3sa2_pnpc_driver); | ||
841 | #endif | ||
842 | return -ENODEV; | ||
843 | } | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | static void __exit alsa_card_opl3sa2_exit(void) | ||
848 | { | ||
849 | int idx; | ||
850 | |||
851 | #ifdef CONFIG_PNP | ||
852 | /* PnP cards first */ | ||
853 | pnp_unregister_card_driver(&opl3sa2_pnpc_driver); | ||
854 | #endif | ||
855 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
856 | snd_card_free(snd_opl3sa2_legacy[idx]); | ||
857 | } | ||
858 | |||
859 | module_init(alsa_card_opl3sa2_init) | ||
860 | module_exit(alsa_card_opl3sa2_exit) | ||
diff --git a/sound/isa/opti9xx/Makefile b/sound/isa/opti9xx/Makefile new file mode 100644 index 000000000000..28c64070cd56 --- /dev/null +++ b/sound/isa/opti9xx/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-opti92x-ad1848-objs := opti92x-ad1848.o | ||
7 | snd-opti92x-cs4231-objs := opti92x-cs4231.o | ||
8 | snd-opti93x-objs := opti93x.o | ||
9 | |||
10 | # Toplevel Module Dependency | ||
11 | obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o | ||
12 | obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o | ||
13 | obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o | ||
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c new file mode 100644 index 000000000000..411a702d85ba --- /dev/null +++ b/sound/isa/opti9xx/opti92x-ad1848.c | |||
@@ -0,0 +1,2226 @@ | |||
1 | /* | ||
2 | card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. | ||
3 | Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it> | ||
4 | |||
5 | Part of this code was developed at the Italian Ministry of Air Defence, | ||
6 | Sixth Division (oh, che pace ...), Rome. | ||
7 | |||
8 | Thanks to Maria Grazia Pollarini, Salvatore Vassallo. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; if not, write to the Free Software | ||
22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | |||
26 | #include <sound/driver.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/dma.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/pnp.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | #include <sound/core.h> | ||
35 | #ifdef CS4231 | ||
36 | #include <sound/cs4231.h> | ||
37 | #else | ||
38 | #ifndef OPTi93X | ||
39 | #include <sound/ad1848.h> | ||
40 | #else | ||
41 | #include <sound/control.h> | ||
42 | #include <sound/pcm.h> | ||
43 | #endif /* OPTi93X */ | ||
44 | #endif /* CS4231 */ | ||
45 | #include <sound/mpu401.h> | ||
46 | #include <sound/opl3.h> | ||
47 | #ifndef OPTi93X | ||
48 | #include <sound/opl4.h> | ||
49 | #endif | ||
50 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
51 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
52 | #include <sound/initval.h> | ||
53 | |||
54 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
55 | MODULE_LICENSE("GPL"); | ||
56 | #ifdef OPTi93X | ||
57 | MODULE_DESCRIPTION("OPTi93X"); | ||
58 | MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}"); | ||
59 | #else /* OPTi93X */ | ||
60 | #ifdef CS4231 | ||
61 | MODULE_DESCRIPTION("OPTi92X - CS4231"); | ||
62 | MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)}," | ||
63 | "{OPTi,82C925 (CS4231)}}"); | ||
64 | #else /* CS4231 */ | ||
65 | MODULE_DESCRIPTION("OPTi92X - AD1848"); | ||
66 | MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)}," | ||
67 | "{OPTi,82C925 (AD1848)}," | ||
68 | "{OAK,Mozart}}"); | ||
69 | #endif /* CS4231 */ | ||
70 | #endif /* OPTi93X */ | ||
71 | |||
72 | static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ | ||
73 | static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ | ||
74 | //static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ | ||
75 | static int isapnp = 1; /* Enable ISA PnP detection */ | ||
76 | static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ | ||
77 | static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ | ||
78 | static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ | ||
79 | static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ | ||
80 | static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ | ||
81 | static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ | ||
82 | #if defined(CS4231) || defined(OPTi93X) | ||
83 | static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ | ||
84 | #endif /* CS4231 || OPTi93X */ | ||
85 | |||
86 | module_param(index, int, 0444); | ||
87 | MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard."); | ||
88 | module_param(id, charp, 0444); | ||
89 | MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); | ||
90 | //module_param(enable, bool, 0444); | ||
91 | //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); | ||
92 | module_param(isapnp, bool, 0444); | ||
93 | MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); | ||
94 | module_param(port, long, 0444); | ||
95 | MODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); | ||
96 | module_param(mpu_port, long, 0444); | ||
97 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver."); | ||
98 | module_param(fm_port, long, 0444); | ||
99 | MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver."); | ||
100 | module_param(irq, int, 0444); | ||
101 | MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver."); | ||
102 | module_param(mpu_irq, int, 0444); | ||
103 | MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver."); | ||
104 | module_param(dma1, int, 0444); | ||
105 | MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver."); | ||
106 | #if defined(CS4231) || defined(OPTi93X) | ||
107 | module_param(dma2, int, 0444); | ||
108 | MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); | ||
109 | #endif /* CS4231 || OPTi93X */ | ||
110 | |||
111 | #define OPTi9XX_HW_DETECT 0 | ||
112 | #define OPTi9XX_HW_82C928 1 | ||
113 | #define OPTi9XX_HW_82C929 2 | ||
114 | #define OPTi9XX_HW_82C924 3 | ||
115 | #define OPTi9XX_HW_82C925 4 | ||
116 | #define OPTi9XX_HW_82C930 5 | ||
117 | #define OPTi9XX_HW_82C931 6 | ||
118 | #define OPTi9XX_HW_82C933 7 | ||
119 | #define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 | ||
120 | |||
121 | #define OPTi9XX_MC_REG(n) n | ||
122 | |||
123 | typedef struct _snd_opti9xx opti9xx_t; | ||
124 | |||
125 | #ifdef OPTi93X | ||
126 | |||
127 | #define OPTi93X_INDEX 0x00 | ||
128 | #define OPTi93X_DATA 0x01 | ||
129 | #define OPTi93X_STATUS 0x02 | ||
130 | #define OPTi93X_DDATA 0x03 | ||
131 | #define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) | ||
132 | |||
133 | #define OPTi93X_MIXOUT_LEFT 0x00 | ||
134 | #define OPTi93X_MIXOUT_RIGHT 0x01 | ||
135 | #define OPTi93X_CD_LEFT_INPUT 0x02 | ||
136 | #define OPTi93X_CD_RIGHT_INPUT 0x03 | ||
137 | #define OPTi930_AUX_LEFT_INPUT 0x04 | ||
138 | #define OPTi930_AUX_RIGHT_INPUT 0x05 | ||
139 | #define OPTi931_FM_LEFT_INPUT 0x04 | ||
140 | #define OPTi931_FM_RIGHT_INPUT 0x05 | ||
141 | #define OPTi93X_DAC_LEFT 0x06 | ||
142 | #define OPTi93X_DAC_RIGHT 0x07 | ||
143 | #define OPTi93X_PLAY_FORMAT 0x08 | ||
144 | #define OPTi93X_IFACE_CONF 0x09 | ||
145 | #define OPTi93X_PIN_CTRL 0x0a | ||
146 | #define OPTi93X_ERR_INIT 0x0b | ||
147 | #define OPTi93X_ID 0x0c | ||
148 | #define OPTi93X_PLAY_UPR_CNT 0x0e | ||
149 | #define OPTi93X_PLAY_LWR_CNT 0x0f | ||
150 | #define OPTi931_AUX_LEFT_INPUT 0x10 | ||
151 | #define OPTi931_AUX_RIGHT_INPUT 0x11 | ||
152 | #define OPTi93X_LINE_LEFT_INPUT 0x12 | ||
153 | #define OPTi93X_LINE_RIGHT_INPUT 0x13 | ||
154 | #define OPTi93X_MIC_LEFT_INPUT 0x14 | ||
155 | #define OPTi93X_MIC_RIGHT_INPUT 0x15 | ||
156 | #define OPTi93X_OUT_LEFT 0x16 | ||
157 | #define OPTi93X_OUT_RIGHT 0x17 | ||
158 | #define OPTi93X_CAPT_FORMAT 0x1c | ||
159 | #define OPTi93X_CAPT_UPR_CNT 0x1e | ||
160 | #define OPTi93X_CAPT_LWR_CNT 0x1f | ||
161 | |||
162 | #define OPTi93X_TRD 0x20 | ||
163 | #define OPTi93X_MCE 0x40 | ||
164 | #define OPTi93X_INIT 0x80 | ||
165 | |||
166 | #define OPTi93X_MIXOUT_MIC_GAIN 0x20 | ||
167 | #define OPTi93X_MIXOUT_LINE 0x00 | ||
168 | #define OPTi93X_MIXOUT_CD 0x40 | ||
169 | #define OPTi93X_MIXOUT_MIC 0x80 | ||
170 | #define OPTi93X_MIXOUT_MIXER 0xc0 | ||
171 | |||
172 | #define OPTi93X_STEREO 0x10 | ||
173 | #define OPTi93X_LINEAR_8 0x00 | ||
174 | #define OPTi93X_ULAW_8 0x20 | ||
175 | #define OPTi93X_LINEAR_16_LIT 0x40 | ||
176 | #define OPTi93X_ALAW_8 0x60 | ||
177 | #define OPTi93X_ADPCM_16 0xa0 | ||
178 | #define OPTi93X_LINEAR_16_BIG 0xc0 | ||
179 | |||
180 | #define OPTi93X_CAPTURE_PIO 0x80 | ||
181 | #define OPTi93X_PLAYBACK_PIO 0x40 | ||
182 | #define OPTi93X_AUTOCALIB 0x08 | ||
183 | #define OPTi93X_SINGLE_DMA 0x04 | ||
184 | #define OPTi93X_CAPTURE_ENABLE 0x02 | ||
185 | #define OPTi93X_PLAYBACK_ENABLE 0x01 | ||
186 | |||
187 | #define OPTi93X_IRQ_ENABLE 0x02 | ||
188 | |||
189 | #define OPTi93X_DMA_REQUEST 0x10 | ||
190 | #define OPTi93X_CALIB_IN_PROGRESS 0x20 | ||
191 | |||
192 | #define OPTi93X_IRQ_PLAYBACK 0x04 | ||
193 | #define OPTi93X_IRQ_CAPTURE 0x08 | ||
194 | |||
195 | |||
196 | typedef struct _snd_opti93x opti93x_t; | ||
197 | |||
198 | struct _snd_opti93x { | ||
199 | unsigned long port; | ||
200 | struct resource *res_port; | ||
201 | int irq; | ||
202 | int dma1; | ||
203 | int dma2; | ||
204 | |||
205 | opti9xx_t *chip; | ||
206 | unsigned short hardware; | ||
207 | unsigned char image[32]; | ||
208 | |||
209 | unsigned char mce_bit; | ||
210 | unsigned short mode; | ||
211 | int mute; | ||
212 | |||
213 | spinlock_t lock; | ||
214 | |||
215 | snd_card_t *card; | ||
216 | snd_pcm_t *pcm; | ||
217 | snd_pcm_substream_t *playback_substream; | ||
218 | snd_pcm_substream_t *capture_substream; | ||
219 | unsigned int p_dma_size; | ||
220 | unsigned int c_dma_size; | ||
221 | }; | ||
222 | |||
223 | #define OPTi93X_MODE_NONE 0x00 | ||
224 | #define OPTi93X_MODE_PLAY 0x01 | ||
225 | #define OPTi93X_MODE_CAPTURE 0x02 | ||
226 | #define OPTi93X_MODE_OPEN (OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE) | ||
227 | |||
228 | #endif /* OPTi93X */ | ||
229 | |||
230 | struct _snd_opti9xx { | ||
231 | unsigned short hardware; | ||
232 | unsigned char password; | ||
233 | char name[7]; | ||
234 | |||
235 | unsigned long mc_base; | ||
236 | struct resource *res_mc_base; | ||
237 | unsigned long mc_base_size; | ||
238 | #ifdef OPTi93X | ||
239 | unsigned long mc_indir_index; | ||
240 | #endif /* OPTi93X */ | ||
241 | unsigned long pwd_reg; | ||
242 | |||
243 | spinlock_t lock; | ||
244 | |||
245 | long wss_base; | ||
246 | int irq; | ||
247 | int dma1; | ||
248 | #if defined(CS4231) || defined(OPTi93X) | ||
249 | int dma2; | ||
250 | #endif /* CS4231 || OPTi93X */ | ||
251 | |||
252 | long fm_port; | ||
253 | |||
254 | long mpu_port; | ||
255 | int mpu_irq; | ||
256 | |||
257 | #ifdef CONFIG_PNP | ||
258 | struct pnp_dev *dev; | ||
259 | struct pnp_dev *devmpu; | ||
260 | #endif /* CONFIG_PNP */ | ||
261 | }; | ||
262 | |||
263 | static int snd_opti9xx_first_hit = 1; | ||
264 | static snd_card_t *snd_opti9xx_legacy = SNDRV_DEFAULT_PTR1; | ||
265 | |||
266 | #ifdef CONFIG_PNP | ||
267 | |||
268 | static struct pnp_card_device_id snd_opti9xx_pnpids[] = { | ||
269 | #ifndef OPTi93X | ||
270 | /* OPTi 82C924 */ | ||
271 | { .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 }, | ||
272 | /* OPTi 82C925 */ | ||
273 | { .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 }, | ||
274 | #else | ||
275 | /* OPTi 82C931/3 */ | ||
276 | { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 }, | ||
277 | #endif /* OPTi93X */ | ||
278 | { .id = "" } | ||
279 | }; | ||
280 | |||
281 | MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); | ||
282 | |||
283 | #endif /* CONFIG_PNP */ | ||
284 | |||
285 | #ifdef OPTi93X | ||
286 | #define DRIVER_NAME "snd-card-opti93x" | ||
287 | #else | ||
288 | #define DRIVER_NAME "snd-card-opti92x" | ||
289 | #endif /* OPTi93X */ | ||
290 | |||
291 | static char * snd_opti9xx_names[] = { | ||
292 | "unkown", | ||
293 | "82C928", "82C929", | ||
294 | "82C924", "82C925", | ||
295 | "82C930", "82C931", "82C933" | ||
296 | }; | ||
297 | |||
298 | |||
299 | static long snd_legacy_find_free_ioport(long *port_table, long size) | ||
300 | { | ||
301 | while (*port_table != -1) { | ||
302 | struct resource *res; | ||
303 | if ((res = request_region(*port_table, size, "ALSA test")) != NULL) { | ||
304 | release_resource(res); | ||
305 | kfree_nocheck(res); | ||
306 | return *port_table; | ||
307 | } | ||
308 | port_table++; | ||
309 | } | ||
310 | return -1; | ||
311 | } | ||
312 | |||
313 | static int __devinit snd_opti9xx_init(opti9xx_t *chip, unsigned short hardware) | ||
314 | { | ||
315 | static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; | ||
316 | |||
317 | chip->hardware = hardware; | ||
318 | strcpy(chip->name, snd_opti9xx_names[hardware]); | ||
319 | |||
320 | chip->mc_base_size = opti9xx_mc_size[hardware]; | ||
321 | |||
322 | spin_lock_init(&chip->lock); | ||
323 | |||
324 | chip->wss_base = -1; | ||
325 | chip->irq = -1; | ||
326 | chip->dma1 = -1; | ||
327 | #if defined(CS4231) || defined (OPTi93X) | ||
328 | chip->dma2 = -1; | ||
329 | #endif /* CS4231 || OPTi93X */ | ||
330 | chip->fm_port = -1; | ||
331 | chip->mpu_port = -1; | ||
332 | chip->mpu_irq = -1; | ||
333 | |||
334 | switch (hardware) { | ||
335 | #ifndef OPTi93X | ||
336 | case OPTi9XX_HW_82C928: | ||
337 | case OPTi9XX_HW_82C929: | ||
338 | chip->mc_base = 0xf8c; | ||
339 | chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; | ||
340 | chip->pwd_reg = 3; | ||
341 | break; | ||
342 | |||
343 | case OPTi9XX_HW_82C924: | ||
344 | case OPTi9XX_HW_82C925: | ||
345 | chip->mc_base = 0xf8c; | ||
346 | chip->password = 0xe5; | ||
347 | chip->pwd_reg = 3; | ||
348 | break; | ||
349 | #else /* OPTi93X */ | ||
350 | |||
351 | case OPTi9XX_HW_82C930: | ||
352 | case OPTi9XX_HW_82C931: | ||
353 | case OPTi9XX_HW_82C933: | ||
354 | chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; | ||
355 | chip->mc_indir_index = 0xe0e; | ||
356 | chip->password = 0xe4; | ||
357 | chip->pwd_reg = 0; | ||
358 | break; | ||
359 | #endif /* OPTi93X */ | ||
360 | |||
361 | default: | ||
362 | snd_printk("chip %d not supported\n", hardware); | ||
363 | return -ENODEV; | ||
364 | } | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static unsigned char snd_opti9xx_read(opti9xx_t *chip, | ||
369 | unsigned char reg) | ||
370 | { | ||
371 | unsigned long flags; | ||
372 | unsigned char retval = 0xff; | ||
373 | |||
374 | spin_lock_irqsave(&chip->lock, flags); | ||
375 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
376 | |||
377 | switch (chip->hardware) { | ||
378 | #ifndef OPTi93X | ||
379 | case OPTi9XX_HW_82C924: | ||
380 | case OPTi9XX_HW_82C925: | ||
381 | if (reg > 7) { | ||
382 | outb(reg, chip->mc_base + 8); | ||
383 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
384 | retval = inb(chip->mc_base + 9); | ||
385 | break; | ||
386 | } | ||
387 | |||
388 | case OPTi9XX_HW_82C928: | ||
389 | case OPTi9XX_HW_82C929: | ||
390 | retval = inb(chip->mc_base + reg); | ||
391 | break; | ||
392 | #else /* OPTi93X */ | ||
393 | |||
394 | case OPTi9XX_HW_82C930: | ||
395 | case OPTi9XX_HW_82C931: | ||
396 | case OPTi9XX_HW_82C933: | ||
397 | outb(reg, chip->mc_indir_index); | ||
398 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
399 | retval = inb(chip->mc_indir_index + 1); | ||
400 | break; | ||
401 | #endif /* OPTi93X */ | ||
402 | |||
403 | default: | ||
404 | snd_printk("chip %d not supported\n", chip->hardware); | ||
405 | } | ||
406 | |||
407 | spin_unlock_irqrestore(&chip->lock, flags); | ||
408 | return retval; | ||
409 | } | ||
410 | |||
411 | static void snd_opti9xx_write(opti9xx_t *chip, unsigned char reg, | ||
412 | unsigned char value) | ||
413 | { | ||
414 | unsigned long flags; | ||
415 | |||
416 | spin_lock_irqsave(&chip->lock, flags); | ||
417 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
418 | |||
419 | switch (chip->hardware) { | ||
420 | #ifndef OPTi93X | ||
421 | case OPTi9XX_HW_82C924: | ||
422 | case OPTi9XX_HW_82C925: | ||
423 | if (reg > 7) { | ||
424 | outb(reg, chip->mc_base + 8); | ||
425 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
426 | outb(value, chip->mc_base + 9); | ||
427 | break; | ||
428 | } | ||
429 | |||
430 | case OPTi9XX_HW_82C928: | ||
431 | case OPTi9XX_HW_82C929: | ||
432 | outb(value, chip->mc_base + reg); | ||
433 | break; | ||
434 | #else /* OPTi93X */ | ||
435 | |||
436 | case OPTi9XX_HW_82C930: | ||
437 | case OPTi9XX_HW_82C931: | ||
438 | case OPTi9XX_HW_82C933: | ||
439 | outb(reg, chip->mc_indir_index); | ||
440 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
441 | outb(value, chip->mc_indir_index + 1); | ||
442 | break; | ||
443 | #endif /* OPTi93X */ | ||
444 | |||
445 | default: | ||
446 | snd_printk("chip %d not supported\n", chip->hardware); | ||
447 | } | ||
448 | |||
449 | spin_unlock_irqrestore(&chip->lock, flags); | ||
450 | } | ||
451 | |||
452 | |||
453 | #define snd_opti9xx_write_mask(chip, reg, value, mask) \ | ||
454 | snd_opti9xx_write(chip, reg, \ | ||
455 | (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) | ||
456 | |||
457 | |||
458 | static int __devinit snd_opti9xx_configure(opti9xx_t *chip) | ||
459 | { | ||
460 | unsigned char wss_base_bits; | ||
461 | unsigned char irq_bits; | ||
462 | unsigned char dma_bits; | ||
463 | unsigned char mpu_port_bits = 0; | ||
464 | unsigned char mpu_irq_bits; | ||
465 | |||
466 | switch (chip->hardware) { | ||
467 | #ifndef OPTi93X | ||
468 | case OPTi9XX_HW_82C924: | ||
469 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); | ||
470 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); | ||
471 | |||
472 | case OPTi9XX_HW_82C925: | ||
473 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); | ||
474 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); | ||
475 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); | ||
476 | #ifdef CS4231 | ||
477 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); | ||
478 | #else | ||
479 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); | ||
480 | #endif /* CS4231 */ | ||
481 | break; | ||
482 | |||
483 | case OPTi9XX_HW_82C928: | ||
484 | case OPTi9XX_HW_82C929: | ||
485 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); | ||
486 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); | ||
487 | /* | ||
488 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); | ||
489 | */ | ||
490 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); | ||
491 | #ifdef CS4231 | ||
492 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); | ||
493 | #else | ||
494 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); | ||
495 | #endif /* CS4231 */ | ||
496 | break; | ||
497 | |||
498 | #else /* OPTi93X */ | ||
499 | case OPTi9XX_HW_82C930: | ||
500 | case OPTi9XX_HW_82C931: | ||
501 | case OPTi9XX_HW_82C933: | ||
502 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); | ||
503 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); | ||
504 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | | ||
505 | (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), | ||
506 | 0x34); | ||
507 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); | ||
508 | break; | ||
509 | #endif /* OPTi93X */ | ||
510 | |||
511 | default: | ||
512 | snd_printk("chip %d not supported\n", chip->hardware); | ||
513 | return -EINVAL; | ||
514 | } | ||
515 | |||
516 | switch (chip->wss_base) { | ||
517 | case 0x530: | ||
518 | wss_base_bits = 0x00; | ||
519 | break; | ||
520 | case 0x604: | ||
521 | wss_base_bits = 0x03; | ||
522 | break; | ||
523 | case 0xe80: | ||
524 | wss_base_bits = 0x01; | ||
525 | break; | ||
526 | case 0xf40: | ||
527 | wss_base_bits = 0x02; | ||
528 | break; | ||
529 | default: | ||
530 | snd_printk("WSS port 0x%lx not valid\n", chip->wss_base); | ||
531 | goto __skip_base; | ||
532 | } | ||
533 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); | ||
534 | |||
535 | __skip_base: | ||
536 | switch (chip->irq) { | ||
537 | //#ifdef OPTi93X | ||
538 | case 5: | ||
539 | irq_bits = 0x05; | ||
540 | break; | ||
541 | //#endif /* OPTi93X */ | ||
542 | case 7: | ||
543 | irq_bits = 0x01; | ||
544 | break; | ||
545 | case 9: | ||
546 | irq_bits = 0x02; | ||
547 | break; | ||
548 | case 10: | ||
549 | irq_bits = 0x03; | ||
550 | break; | ||
551 | case 11: | ||
552 | irq_bits = 0x04; | ||
553 | break; | ||
554 | default: | ||
555 | snd_printk("WSS irq # %d not valid\n", chip->irq); | ||
556 | goto __skip_resources; | ||
557 | } | ||
558 | |||
559 | switch (chip->dma1) { | ||
560 | case 0: | ||
561 | dma_bits = 0x01; | ||
562 | break; | ||
563 | case 1: | ||
564 | dma_bits = 0x02; | ||
565 | break; | ||
566 | case 3: | ||
567 | dma_bits = 0x03; | ||
568 | break; | ||
569 | default: | ||
570 | snd_printk("WSS dma1 # %d not valid\n", chip->dma1); | ||
571 | goto __skip_resources; | ||
572 | } | ||
573 | |||
574 | #if defined(CS4231) || defined(OPTi93X) | ||
575 | if (chip->dma1 == chip->dma2) { | ||
576 | snd_printk("don't want to share dmas\n"); | ||
577 | return -EBUSY; | ||
578 | } | ||
579 | |||
580 | switch (chip->dma2) { | ||
581 | case 0: | ||
582 | case 1: | ||
583 | break; | ||
584 | default: | ||
585 | snd_printk("WSS dma2 # %d not valid\n", chip->dma2); | ||
586 | goto __skip_resources; | ||
587 | } | ||
588 | dma_bits |= 0x04; | ||
589 | #endif /* CS4231 || OPTi93X */ | ||
590 | |||
591 | #ifndef OPTi93X | ||
592 | outb(irq_bits << 3 | dma_bits, chip->wss_base); | ||
593 | #else /* OPTi93X */ | ||
594 | snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); | ||
595 | #endif /* OPTi93X */ | ||
596 | |||
597 | __skip_resources: | ||
598 | if (chip->hardware > OPTi9XX_HW_82C928) { | ||
599 | switch (chip->mpu_port) { | ||
600 | case 0: | ||
601 | case -1: | ||
602 | break; | ||
603 | case 0x300: | ||
604 | mpu_port_bits = 0x03; | ||
605 | break; | ||
606 | case 0x310: | ||
607 | mpu_port_bits = 0x02; | ||
608 | break; | ||
609 | case 0x320: | ||
610 | mpu_port_bits = 0x01; | ||
611 | break; | ||
612 | case 0x330: | ||
613 | mpu_port_bits = 0x00; | ||
614 | break; | ||
615 | default: | ||
616 | snd_printk("MPU-401 port 0x%lx not valid\n", | ||
617 | chip->mpu_port); | ||
618 | goto __skip_mpu; | ||
619 | } | ||
620 | |||
621 | switch (chip->mpu_irq) { | ||
622 | case 5: | ||
623 | mpu_irq_bits = 0x02; | ||
624 | break; | ||
625 | case 7: | ||
626 | mpu_irq_bits = 0x03; | ||
627 | break; | ||
628 | case 9: | ||
629 | mpu_irq_bits = 0x00; | ||
630 | break; | ||
631 | case 10: | ||
632 | mpu_irq_bits = 0x01; | ||
633 | break; | ||
634 | default: | ||
635 | snd_printk("MPU-401 irq # %d not valid\n", | ||
636 | chip->mpu_irq); | ||
637 | goto __skip_mpu; | ||
638 | } | ||
639 | |||
640 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), | ||
641 | (chip->mpu_port <= 0) ? 0x00 : | ||
642 | 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, | ||
643 | 0xf8); | ||
644 | } | ||
645 | __skip_mpu: | ||
646 | |||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | #ifdef OPTi93X | ||
651 | |||
652 | static unsigned char snd_opti93x_default_image[32] = | ||
653 | { | ||
654 | 0x00, /* 00/00 - l_mixout_outctrl */ | ||
655 | 0x00, /* 01/01 - r_mixout_outctrl */ | ||
656 | 0x88, /* 02/02 - l_cd_inctrl */ | ||
657 | 0x88, /* 03/03 - r_cd_inctrl */ | ||
658 | 0x88, /* 04/04 - l_a1/fm_inctrl */ | ||
659 | 0x88, /* 05/05 - r_a1/fm_inctrl */ | ||
660 | 0x80, /* 06/06 - l_dac_inctrl */ | ||
661 | 0x80, /* 07/07 - r_dac_inctrl */ | ||
662 | 0x00, /* 08/08 - ply_dataform_reg */ | ||
663 | 0x00, /* 09/09 - if_conf */ | ||
664 | 0x00, /* 0a/10 - pin_ctrl */ | ||
665 | 0x00, /* 0b/11 - err_init_reg */ | ||
666 | 0x0a, /* 0c/12 - id_reg */ | ||
667 | 0x00, /* 0d/13 - reserved */ | ||
668 | 0x00, /* 0e/14 - ply_upcount_reg */ | ||
669 | 0x00, /* 0f/15 - ply_lowcount_reg */ | ||
670 | 0x88, /* 10/16 - reserved/l_a1_inctrl */ | ||
671 | 0x88, /* 11/17 - reserved/r_a1_inctrl */ | ||
672 | 0x88, /* 12/18 - l_line_inctrl */ | ||
673 | 0x88, /* 13/19 - r_line_inctrl */ | ||
674 | 0x88, /* 14/20 - l_mic_inctrl */ | ||
675 | 0x88, /* 15/21 - r_mic_inctrl */ | ||
676 | 0x80, /* 16/22 - l_out_outctrl */ | ||
677 | 0x80, /* 17/23 - r_out_outctrl */ | ||
678 | 0x00, /* 18/24 - reserved */ | ||
679 | 0x00, /* 19/25 - reserved */ | ||
680 | 0x00, /* 1a/26 - reserved */ | ||
681 | 0x00, /* 1b/27 - reserved */ | ||
682 | 0x00, /* 1c/28 - cap_dataform_reg */ | ||
683 | 0x00, /* 1d/29 - reserved */ | ||
684 | 0x00, /* 1e/30 - cap_upcount_reg */ | ||
685 | 0x00 /* 1f/31 - cap_lowcount_reg */ | ||
686 | }; | ||
687 | |||
688 | |||
689 | static int snd_opti93x_busy_wait(opti93x_t *chip) | ||
690 | { | ||
691 | int timeout; | ||
692 | |||
693 | for (timeout = 250; timeout-- > 0; udelay(10)) | ||
694 | if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT)) | ||
695 | return 0; | ||
696 | |||
697 | snd_printk("chip still busy.\n"); | ||
698 | return -EBUSY; | ||
699 | } | ||
700 | |||
701 | static unsigned char snd_opti93x_in(opti93x_t *chip, unsigned char reg) | ||
702 | { | ||
703 | snd_opti93x_busy_wait(chip); | ||
704 | outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); | ||
705 | return inb(OPTi93X_PORT(chip, DATA)); | ||
706 | } | ||
707 | |||
708 | static void snd_opti93x_out(opti93x_t *chip, unsigned char reg, | ||
709 | unsigned char value) | ||
710 | { | ||
711 | snd_opti93x_busy_wait(chip); | ||
712 | outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); | ||
713 | outb(value, OPTi93X_PORT(chip, DATA)); | ||
714 | } | ||
715 | |||
716 | static void snd_opti93x_out_image(opti93x_t *chip, unsigned char reg, | ||
717 | unsigned char value) | ||
718 | { | ||
719 | snd_opti93x_out(chip, reg, chip->image[reg] = value); | ||
720 | } | ||
721 | |||
722 | static void snd_opti93x_out_mask(opti93x_t *chip, unsigned char reg, | ||
723 | unsigned char mask, unsigned char value) | ||
724 | { | ||
725 | snd_opti93x_out_image(chip, reg, | ||
726 | (chip->image[reg] & ~mask) | (value & mask)); | ||
727 | } | ||
728 | |||
729 | |||
730 | static void snd_opti93x_mce_up(opti93x_t *chip) | ||
731 | { | ||
732 | snd_opti93x_busy_wait(chip); | ||
733 | |||
734 | chip->mce_bit = OPTi93X_MCE; | ||
735 | if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)) | ||
736 | outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); | ||
737 | } | ||
738 | |||
739 | static void snd_opti93x_mce_down(opti93x_t *chip) | ||
740 | { | ||
741 | snd_opti93x_busy_wait(chip); | ||
742 | |||
743 | chip->mce_bit = 0; | ||
744 | if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE) | ||
745 | outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); | ||
746 | } | ||
747 | |||
748 | #define snd_opti93x_mute_reg(chip, reg, mute) \ | ||
749 | snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]); | ||
750 | |||
751 | static void snd_opti93x_mute(opti93x_t *chip, int mute) | ||
752 | { | ||
753 | mute = mute ? 1 : 0; | ||
754 | if (chip->mute == mute) | ||
755 | return; | ||
756 | |||
757 | chip->mute = mute; | ||
758 | |||
759 | snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute); | ||
760 | snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute); | ||
761 | switch (chip->hardware) { | ||
762 | case OPTi9XX_HW_82C930: | ||
763 | snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute); | ||
764 | snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute); | ||
765 | break; | ||
766 | case OPTi9XX_HW_82C931: | ||
767 | case OPTi9XX_HW_82C933: | ||
768 | snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute); | ||
769 | snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute); | ||
770 | snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute); | ||
771 | snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute); | ||
772 | } | ||
773 | snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute); | ||
774 | snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute); | ||
775 | snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute); | ||
776 | snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute); | ||
777 | snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute); | ||
778 | snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute); | ||
779 | snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute); | ||
780 | snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute); | ||
781 | } | ||
782 | |||
783 | |||
784 | static unsigned int snd_opti93x_get_count(unsigned char format, | ||
785 | unsigned int size) | ||
786 | { | ||
787 | switch (format & 0xe0) { | ||
788 | case OPTi93X_LINEAR_16_LIT: | ||
789 | case OPTi93X_LINEAR_16_BIG: | ||
790 | size >>= 1; | ||
791 | break; | ||
792 | case OPTi93X_ADPCM_16: | ||
793 | return size >> 2; | ||
794 | } | ||
795 | return (format & OPTi93X_STEREO) ? (size >> 1) : size; | ||
796 | } | ||
797 | |||
798 | static unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, | ||
799 | 18900, 22050, 27428, 32000, 33075, 37800, | ||
800 | 44100, 48000 }; | ||
801 | #define RATES ARRAY_SIZE(rates) | ||
802 | |||
803 | static snd_pcm_hw_constraint_list_t hw_constraints_rates = { | ||
804 | .count = RATES, | ||
805 | .list = rates, | ||
806 | .mask = 0, | ||
807 | }; | ||
808 | |||
809 | static unsigned char bits[] = { 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, | ||
810 | 0x05, 0x07, 0x04, 0x06, 0x0d, 0x09, | ||
811 | 0x0b, 0x0c}; | ||
812 | |||
813 | static unsigned char snd_opti93x_get_freq(unsigned int rate) | ||
814 | { | ||
815 | unsigned int i; | ||
816 | |||
817 | for (i = 0; i < RATES; i++) { | ||
818 | if (rate == rates[i]) | ||
819 | return bits[i]; | ||
820 | } | ||
821 | snd_BUG(); | ||
822 | return bits[RATES-1]; | ||
823 | } | ||
824 | |||
825 | static unsigned char snd_opti93x_get_format(opti93x_t *chip, | ||
826 | unsigned int format, int channels) | ||
827 | { | ||
828 | unsigned char retval = OPTi93X_LINEAR_8; | ||
829 | |||
830 | switch (format) { | ||
831 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
832 | retval = OPTi93X_ULAW_8; | ||
833 | break; | ||
834 | case SNDRV_PCM_FORMAT_A_LAW: | ||
835 | retval = OPTi93X_ALAW_8; | ||
836 | break; | ||
837 | case SNDRV_PCM_FORMAT_S16_LE: | ||
838 | retval = OPTi93X_LINEAR_16_LIT; | ||
839 | break; | ||
840 | case SNDRV_PCM_FORMAT_S16_BE: | ||
841 | retval = OPTi93X_LINEAR_16_BIG; | ||
842 | break; | ||
843 | case SNDRV_PCM_FORMAT_IMA_ADPCM: | ||
844 | retval = OPTi93X_ADPCM_16; | ||
845 | } | ||
846 | return (channels > 1) ? (retval | OPTi93X_STEREO) : retval; | ||
847 | } | ||
848 | |||
849 | |||
850 | static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt) | ||
851 | { | ||
852 | unsigned char mask; | ||
853 | |||
854 | snd_opti93x_mute(chip, 1); | ||
855 | |||
856 | snd_opti93x_mce_up(chip); | ||
857 | mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff; | ||
858 | snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt); | ||
859 | snd_opti93x_mce_down(chip); | ||
860 | |||
861 | snd_opti93x_mute(chip, 0); | ||
862 | } | ||
863 | |||
864 | static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt) | ||
865 | { | ||
866 | snd_opti93x_mute(chip, 1); | ||
867 | |||
868 | snd_opti93x_mce_up(chip); | ||
869 | if (!(chip->mode & OPTi93X_MODE_PLAY)) | ||
870 | snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt); | ||
871 | else | ||
872 | fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0; | ||
873 | snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt); | ||
874 | snd_opti93x_mce_down(chip); | ||
875 | |||
876 | snd_opti93x_mute(chip, 0); | ||
877 | } | ||
878 | |||
879 | |||
880 | static int snd_opti93x_open(opti93x_t *chip, unsigned int mode) | ||
881 | { | ||
882 | unsigned long flags; | ||
883 | |||
884 | spin_lock_irqsave(&chip->lock, flags); | ||
885 | |||
886 | if (chip->mode & mode) { | ||
887 | spin_unlock_irqrestore(&chip->lock, flags); | ||
888 | return -EAGAIN; | ||
889 | } | ||
890 | |||
891 | if (!(chip->mode & OPTi93X_MODE_OPEN)) { | ||
892 | outb(0x00, OPTi93X_PORT(chip, STATUS)); | ||
893 | snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, | ||
894 | OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE); | ||
895 | chip->mode = mode; | ||
896 | } | ||
897 | else | ||
898 | chip->mode |= mode; | ||
899 | |||
900 | spin_unlock_irqrestore(&chip->lock, flags); | ||
901 | return 0; | ||
902 | } | ||
903 | |||
904 | static void snd_opti93x_close(opti93x_t *chip, unsigned int mode) | ||
905 | { | ||
906 | unsigned long flags; | ||
907 | |||
908 | spin_lock_irqsave(&chip->lock, flags); | ||
909 | |||
910 | chip->mode &= ~mode; | ||
911 | if (chip->mode & OPTi93X_MODE_OPEN) { | ||
912 | spin_unlock_irqrestore(&chip->lock, flags); | ||
913 | return; | ||
914 | } | ||
915 | |||
916 | snd_opti93x_mute(chip, 1); | ||
917 | |||
918 | outb(0, OPTi93X_PORT(chip, STATUS)); | ||
919 | snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE, | ||
920 | ~OPTi93X_IRQ_ENABLE); | ||
921 | |||
922 | snd_opti93x_mce_up(chip); | ||
923 | snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00); | ||
924 | snd_opti93x_mce_down(chip); | ||
925 | chip->mode = 0; | ||
926 | |||
927 | snd_opti93x_mute(chip, 0); | ||
928 | spin_unlock_irqrestore(&chip->lock, flags); | ||
929 | } | ||
930 | |||
931 | static int snd_opti93x_trigger(snd_pcm_substream_t *substream, | ||
932 | unsigned char what, int cmd) | ||
933 | { | ||
934 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
935 | |||
936 | switch (cmd) { | ||
937 | case SNDRV_PCM_TRIGGER_START: | ||
938 | case SNDRV_PCM_TRIGGER_STOP: | ||
939 | { | ||
940 | unsigned int what = 0; | ||
941 | struct list_head *pos; | ||
942 | snd_pcm_substream_t *s; | ||
943 | snd_pcm_group_for_each(pos, substream) { | ||
944 | s = snd_pcm_group_substream_entry(pos); | ||
945 | if (s == chip->playback_substream) { | ||
946 | what |= OPTi93X_PLAYBACK_ENABLE; | ||
947 | snd_pcm_trigger_done(s, substream); | ||
948 | } else if (s == chip->capture_substream) { | ||
949 | what |= OPTi93X_CAPTURE_ENABLE; | ||
950 | snd_pcm_trigger_done(s, substream); | ||
951 | } | ||
952 | } | ||
953 | spin_lock(&chip->lock); | ||
954 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
955 | snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what); | ||
956 | if (what & OPTi93X_CAPTURE_ENABLE) | ||
957 | udelay(50); | ||
958 | } else | ||
959 | snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00); | ||
960 | spin_unlock(&chip->lock); | ||
961 | break; | ||
962 | } | ||
963 | default: | ||
964 | return -EINVAL; | ||
965 | } | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static int snd_opti93x_playback_trigger(snd_pcm_substream_t *substream, int cmd) | ||
970 | { | ||
971 | return snd_opti93x_trigger(substream, | ||
972 | OPTi93X_PLAYBACK_ENABLE, cmd); | ||
973 | } | ||
974 | |||
975 | static int snd_opti93x_capture_trigger(snd_pcm_substream_t * substream, int cmd) | ||
976 | { | ||
977 | return snd_opti93x_trigger(substream, | ||
978 | OPTi93X_CAPTURE_ENABLE, cmd); | ||
979 | } | ||
980 | |||
981 | static int snd_opti93x_hw_params(snd_pcm_substream_t * substream, | ||
982 | snd_pcm_hw_params_t * hw_params) | ||
983 | { | ||
984 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
985 | } | ||
986 | |||
987 | |||
988 | static int snd_opti93x_hw_free(snd_pcm_substream_t * substream) | ||
989 | { | ||
990 | snd_pcm_lib_free_pages(substream); | ||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | |||
995 | static int snd_opti93x_playback_prepare(snd_pcm_substream_t * substream) | ||
996 | { | ||
997 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
998 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
999 | unsigned long flags; | ||
1000 | unsigned char format; | ||
1001 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
1002 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
1003 | |||
1004 | spin_lock_irqsave(&chip->lock, flags); | ||
1005 | |||
1006 | chip->p_dma_size = size; | ||
1007 | snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, | ||
1008 | OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO, | ||
1009 | ~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO)); | ||
1010 | |||
1011 | snd_dma_program(chip->dma1, runtime->dma_addr, size, | ||
1012 | DMA_MODE_WRITE | DMA_AUTOINIT); | ||
1013 | |||
1014 | format = snd_opti93x_get_freq(runtime->rate); | ||
1015 | format |= snd_opti93x_get_format(chip, runtime->format, | ||
1016 | runtime->channels); | ||
1017 | snd_opti93x_playback_format(chip, format); | ||
1018 | format = chip->image[OPTi93X_PLAY_FORMAT]; | ||
1019 | |||
1020 | count = snd_opti93x_get_count(format, count) - 1; | ||
1021 | snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count); | ||
1022 | snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8); | ||
1023 | |||
1024 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1025 | return 0; | ||
1026 | } | ||
1027 | |||
1028 | static int snd_opti93x_capture_prepare(snd_pcm_substream_t *substream) | ||
1029 | { | ||
1030 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1031 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1032 | unsigned long flags; | ||
1033 | unsigned char format; | ||
1034 | unsigned int count = snd_pcm_lib_period_bytes(substream); | ||
1035 | unsigned int size = snd_pcm_lib_buffer_bytes(substream); | ||
1036 | |||
1037 | spin_lock_irqsave(&chip->lock, flags); | ||
1038 | |||
1039 | chip->c_dma_size = size; | ||
1040 | snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, | ||
1041 | OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, | ||
1042 | (unsigned char)~(OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO)); | ||
1043 | |||
1044 | snd_dma_program(chip->dma2, runtime->dma_addr, size, | ||
1045 | DMA_MODE_READ | DMA_AUTOINIT); | ||
1046 | |||
1047 | format = snd_opti93x_get_freq(runtime->rate); | ||
1048 | format |= snd_opti93x_get_format(chip, runtime->format, | ||
1049 | runtime->channels); | ||
1050 | snd_opti93x_capture_format(chip, format); | ||
1051 | format = chip->image[OPTi93X_CAPT_FORMAT]; | ||
1052 | |||
1053 | count = snd_opti93x_get_count(format, count) - 1; | ||
1054 | snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count); | ||
1055 | snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8); | ||
1056 | |||
1057 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1058 | return 0; | ||
1059 | } | ||
1060 | |||
1061 | static snd_pcm_uframes_t snd_opti93x_playback_pointer(snd_pcm_substream_t *substream) | ||
1062 | { | ||
1063 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1064 | size_t ptr; | ||
1065 | |||
1066 | if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE)) | ||
1067 | return 0; | ||
1068 | |||
1069 | ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); | ||
1070 | return bytes_to_frames(substream->runtime, ptr); | ||
1071 | } | ||
1072 | |||
1073 | static snd_pcm_uframes_t snd_opti93x_capture_pointer(snd_pcm_substream_t *substream) | ||
1074 | { | ||
1075 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1076 | size_t ptr; | ||
1077 | |||
1078 | if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE)) | ||
1079 | return 0; | ||
1080 | |||
1081 | ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); | ||
1082 | return bytes_to_frames(substream->runtime, ptr); | ||
1083 | } | ||
1084 | |||
1085 | |||
1086 | static void snd_opti93x_overrange(opti93x_t *chip) | ||
1087 | { | ||
1088 | unsigned long flags; | ||
1089 | |||
1090 | spin_lock_irqsave(&chip->lock, flags); | ||
1091 | |||
1092 | if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02)) | ||
1093 | chip->capture_substream->runtime->overrange++; | ||
1094 | |||
1095 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1096 | } | ||
1097 | |||
1098 | static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
1099 | { | ||
1100 | opti93x_t *codec = dev_id; | ||
1101 | unsigned char status; | ||
1102 | |||
1103 | status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11)); | ||
1104 | if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) | ||
1105 | snd_pcm_period_elapsed(codec->playback_substream); | ||
1106 | if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { | ||
1107 | snd_opti93x_overrange(codec); | ||
1108 | snd_pcm_period_elapsed(codec->capture_substream); | ||
1109 | } | ||
1110 | outb(0x00, OPTi93X_PORT(codec, STATUS)); | ||
1111 | return IRQ_HANDLED; | ||
1112 | } | ||
1113 | |||
1114 | |||
1115 | static snd_pcm_hardware_t snd_opti93x_playback = { | ||
1116 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
1117 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), | ||
1118 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | | ||
1119 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), | ||
1120 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
1121 | .rate_min = 5512, | ||
1122 | .rate_max = 48000, | ||
1123 | .channels_min = 1, | ||
1124 | .channels_max = 2, | ||
1125 | .buffer_bytes_max = (128*1024), | ||
1126 | .period_bytes_min = 64, | ||
1127 | .period_bytes_max = (128*1024), | ||
1128 | .periods_min = 1, | ||
1129 | .periods_max = 1024, | ||
1130 | .fifo_size = 0, | ||
1131 | }; | ||
1132 | |||
1133 | static snd_pcm_hardware_t snd_opti93x_capture = { | ||
1134 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
1135 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), | ||
1136 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | | ||
1137 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), | ||
1138 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
1139 | .rate_min = 5512, | ||
1140 | .rate_max = 48000, | ||
1141 | .channels_min = 1, | ||
1142 | .channels_max = 2, | ||
1143 | .buffer_bytes_max = (128*1024), | ||
1144 | .period_bytes_min = 64, | ||
1145 | .period_bytes_max = (128*1024), | ||
1146 | .periods_min = 1, | ||
1147 | .periods_max = 1024, | ||
1148 | .fifo_size = 0, | ||
1149 | }; | ||
1150 | |||
1151 | static int snd_opti93x_playback_open(snd_pcm_substream_t *substream) | ||
1152 | { | ||
1153 | int error; | ||
1154 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1155 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1156 | |||
1157 | if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0) | ||
1158 | return error; | ||
1159 | snd_pcm_set_sync(substream); | ||
1160 | chip->playback_substream = substream; | ||
1161 | runtime->hw = snd_opti93x_playback; | ||
1162 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); | ||
1163 | snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
1164 | return error; | ||
1165 | } | ||
1166 | |||
1167 | static int snd_opti93x_capture_open(snd_pcm_substream_t *substream) | ||
1168 | { | ||
1169 | int error; | ||
1170 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1171 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
1172 | |||
1173 | if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0) | ||
1174 | return error; | ||
1175 | runtime->hw = snd_opti93x_capture; | ||
1176 | snd_pcm_set_sync(substream); | ||
1177 | chip->capture_substream = substream; | ||
1178 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); | ||
1179 | snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
1180 | return error; | ||
1181 | } | ||
1182 | |||
1183 | static int snd_opti93x_playback_close(snd_pcm_substream_t *substream) | ||
1184 | { | ||
1185 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1186 | |||
1187 | chip->playback_substream = NULL; | ||
1188 | snd_opti93x_close(chip, OPTi93X_MODE_PLAY); | ||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static int snd_opti93x_capture_close(snd_pcm_substream_t *substream) | ||
1193 | { | ||
1194 | opti93x_t *chip = snd_pcm_substream_chip(substream); | ||
1195 | |||
1196 | chip->capture_substream = NULL; | ||
1197 | snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE); | ||
1198 | return 0; | ||
1199 | } | ||
1200 | |||
1201 | |||
1202 | static void snd_opti93x_init(opti93x_t *chip) | ||
1203 | { | ||
1204 | unsigned long flags; | ||
1205 | int i; | ||
1206 | |||
1207 | spin_lock_irqsave(&chip->lock, flags); | ||
1208 | snd_opti93x_mce_up(chip); | ||
1209 | |||
1210 | for (i = 0; i < 32; i++) | ||
1211 | snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]); | ||
1212 | |||
1213 | snd_opti93x_mce_down(chip); | ||
1214 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1215 | } | ||
1216 | |||
1217 | static int snd_opti93x_probe(opti93x_t *chip) | ||
1218 | { | ||
1219 | unsigned long flags; | ||
1220 | unsigned char val; | ||
1221 | |||
1222 | spin_lock_irqsave(&chip->lock, flags); | ||
1223 | val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f; | ||
1224 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1225 | |||
1226 | return (val == 0x0a) ? 0 : -ENODEV; | ||
1227 | } | ||
1228 | |||
1229 | static int snd_opti93x_free(opti93x_t *chip) | ||
1230 | { | ||
1231 | if (chip->res_port) { | ||
1232 | release_resource(chip->res_port); | ||
1233 | kfree_nocheck(chip->res_port); | ||
1234 | } | ||
1235 | if (chip->dma1 >= 0) { | ||
1236 | disable_dma(chip->dma1); | ||
1237 | free_dma(chip->dma1); | ||
1238 | } | ||
1239 | if (chip->dma2 >= 0) { | ||
1240 | disable_dma(chip->dma2); | ||
1241 | free_dma(chip->dma2); | ||
1242 | } | ||
1243 | if (chip->irq >= 0) { | ||
1244 | free_irq(chip->irq, chip); | ||
1245 | } | ||
1246 | kfree(chip); | ||
1247 | return 0; | ||
1248 | } | ||
1249 | |||
1250 | static int snd_opti93x_dev_free(snd_device_t *device) | ||
1251 | { | ||
1252 | opti93x_t *chip = device->device_data; | ||
1253 | return snd_opti93x_free(chip); | ||
1254 | } | ||
1255 | |||
1256 | static const char *snd_opti93x_chip_id(opti93x_t *codec) | ||
1257 | { | ||
1258 | switch (codec->hardware) { | ||
1259 | case OPTi9XX_HW_82C930: return "82C930"; | ||
1260 | case OPTi9XX_HW_82C931: return "82C931"; | ||
1261 | case OPTi9XX_HW_82C933: return "82C933"; | ||
1262 | default: return "???"; | ||
1263 | } | ||
1264 | } | ||
1265 | |||
1266 | static int snd_opti93x_create(snd_card_t *card, opti9xx_t *chip, | ||
1267 | int dma1, int dma2, | ||
1268 | opti93x_t **rcodec) | ||
1269 | { | ||
1270 | static snd_device_ops_t ops = { | ||
1271 | .dev_free = snd_opti93x_dev_free, | ||
1272 | }; | ||
1273 | int error; | ||
1274 | opti93x_t *codec; | ||
1275 | |||
1276 | *rcodec = NULL; | ||
1277 | codec = kcalloc(1, sizeof(*codec), GFP_KERNEL); | ||
1278 | if (codec == NULL) | ||
1279 | return -ENOMEM; | ||
1280 | codec->irq = -1; | ||
1281 | codec->dma1 = -1; | ||
1282 | codec->dma2 = -1; | ||
1283 | |||
1284 | if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) { | ||
1285 | snd_printk(KERN_ERR "opti9xx: can't grab port 0x%lx\n", chip->wss_base + 4); | ||
1286 | snd_opti93x_free(codec); | ||
1287 | return -EBUSY; | ||
1288 | } | ||
1289 | if (request_dma(dma1, "OPTI93x - 1")) { | ||
1290 | snd_printk(KERN_ERR "opti9xx: can't grab DMA1 %d\n", dma1); | ||
1291 | snd_opti93x_free(codec); | ||
1292 | return -EBUSY; | ||
1293 | } | ||
1294 | codec->dma1 = chip->dma1; | ||
1295 | if (request_dma(dma2, "OPTI93x - 2")) { | ||
1296 | snd_printk(KERN_ERR "opti9xx: can't grab DMA2 %d\n", dma2); | ||
1297 | snd_opti93x_free(codec); | ||
1298 | return -EBUSY; | ||
1299 | } | ||
1300 | codec->dma2 = chip->dma2; | ||
1301 | |||
1302 | if (request_irq(chip->irq, snd_opti93x_interrupt, SA_INTERRUPT, DRIVER_NAME" - WSS", codec)) { | ||
1303 | snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq); | ||
1304 | snd_opti93x_free(codec); | ||
1305 | return -EBUSY; | ||
1306 | } | ||
1307 | |||
1308 | codec->card = card; | ||
1309 | codec->port = chip->wss_base + 4; | ||
1310 | codec->irq = chip->irq; | ||
1311 | |||
1312 | spin_lock_init(&codec->lock); | ||
1313 | codec->hardware = chip->hardware; | ||
1314 | codec->chip = chip; | ||
1315 | |||
1316 | if ((error = snd_opti93x_probe(codec))) { | ||
1317 | snd_opti93x_free(codec); | ||
1318 | return error; | ||
1319 | } | ||
1320 | |||
1321 | snd_opti93x_init(codec); | ||
1322 | |||
1323 | /* Register device */ | ||
1324 | if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) { | ||
1325 | snd_opti93x_free(codec); | ||
1326 | return error; | ||
1327 | } | ||
1328 | |||
1329 | *rcodec = codec; | ||
1330 | return 0; | ||
1331 | } | ||
1332 | |||
1333 | static snd_pcm_ops_t snd_opti93x_playback_ops = { | ||
1334 | .open = snd_opti93x_playback_open, | ||
1335 | .close = snd_opti93x_playback_close, | ||
1336 | .ioctl = snd_pcm_lib_ioctl, | ||
1337 | .hw_params = snd_opti93x_hw_params, | ||
1338 | .hw_free = snd_opti93x_hw_free, | ||
1339 | .prepare = snd_opti93x_playback_prepare, | ||
1340 | .trigger = snd_opti93x_playback_trigger, | ||
1341 | .pointer = snd_opti93x_playback_pointer, | ||
1342 | }; | ||
1343 | |||
1344 | static snd_pcm_ops_t snd_opti93x_capture_ops = { | ||
1345 | .open = snd_opti93x_capture_open, | ||
1346 | .close = snd_opti93x_capture_close, | ||
1347 | .ioctl = snd_pcm_lib_ioctl, | ||
1348 | .hw_params = snd_opti93x_hw_params, | ||
1349 | .hw_free = snd_opti93x_hw_free, | ||
1350 | .prepare = snd_opti93x_capture_prepare, | ||
1351 | .trigger = snd_opti93x_capture_trigger, | ||
1352 | .pointer = snd_opti93x_capture_pointer, | ||
1353 | }; | ||
1354 | |||
1355 | static void snd_opti93x_pcm_free(snd_pcm_t *pcm) | ||
1356 | { | ||
1357 | opti93x_t *codec = pcm->private_data; | ||
1358 | codec->pcm = NULL; | ||
1359 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
1360 | } | ||
1361 | |||
1362 | static int snd_opti93x_pcm(opti93x_t *codec, int device, snd_pcm_t **rpcm) | ||
1363 | { | ||
1364 | int error; | ||
1365 | snd_pcm_t *pcm; | ||
1366 | |||
1367 | if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm))) | ||
1368 | return error; | ||
1369 | |||
1370 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops); | ||
1371 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops); | ||
1372 | |||
1373 | pcm->private_data = codec; | ||
1374 | pcm->private_free = snd_opti93x_pcm_free; | ||
1375 | pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
1376 | |||
1377 | strcpy(pcm->name, snd_opti93x_chip_id(codec)); | ||
1378 | |||
1379 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
1380 | snd_dma_isa_data(), | ||
1381 | 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); | ||
1382 | |||
1383 | codec->pcm = pcm; | ||
1384 | if (rpcm) | ||
1385 | *rpcm = pcm; | ||
1386 | return 0; | ||
1387 | } | ||
1388 | |||
1389 | /* | ||
1390 | * MIXER part | ||
1391 | */ | ||
1392 | |||
1393 | static int snd_opti93x_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1394 | { | ||
1395 | static char *texts[4] = { | ||
1396 | "Line1", "Aux", "Mic", "Mix" | ||
1397 | }; | ||
1398 | |||
1399 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
1400 | uinfo->count = 2; | ||
1401 | uinfo->value.enumerated.items = 4; | ||
1402 | if (uinfo->value.enumerated.item > 3) | ||
1403 | uinfo->value.enumerated.item = 3; | ||
1404 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
1405 | return 0; | ||
1406 | } | ||
1407 | |||
1408 | static int snd_opti93x_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1409 | { | ||
1410 | opti93x_t *chip = snd_kcontrol_chip(kcontrol); | ||
1411 | unsigned long flags; | ||
1412 | |||
1413 | spin_lock_irqsave(&chip->lock, flags); | ||
1414 | ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6; | ||
1415 | ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6; | ||
1416 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1417 | return 0; | ||
1418 | } | ||
1419 | |||
1420 | static int snd_opti93x_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1421 | { | ||
1422 | opti93x_t *chip = snd_kcontrol_chip(kcontrol); | ||
1423 | unsigned long flags; | ||
1424 | unsigned short left, right; | ||
1425 | int change; | ||
1426 | |||
1427 | if (ucontrol->value.enumerated.item[0] > 3 || | ||
1428 | ucontrol->value.enumerated.item[1] > 3) | ||
1429 | return -EINVAL; | ||
1430 | left = ucontrol->value.enumerated.item[0] << 6; | ||
1431 | right = ucontrol->value.enumerated.item[1] << 6; | ||
1432 | spin_lock_irqsave(&chip->lock, flags); | ||
1433 | left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left; | ||
1434 | right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right; | ||
1435 | change = left != chip->image[OPTi93X_MIXOUT_LEFT] || | ||
1436 | right != chip->image[OPTi93X_MIXOUT_RIGHT]; | ||
1437 | snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left); | ||
1438 | snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right); | ||
1439 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1440 | return change; | ||
1441 | } | ||
1442 | |||
1443 | #if 0 | ||
1444 | |||
1445 | #define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \ | ||
1446 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
1447 | .info = snd_opti93x_info_single, \ | ||
1448 | .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \ | ||
1449 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
1450 | |||
1451 | static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1452 | { | ||
1453 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1454 | |||
1455 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1456 | uinfo->count = 1; | ||
1457 | uinfo->value.integer.min = 0; | ||
1458 | uinfo->value.integer.max = mask; | ||
1459 | return 0; | ||
1460 | } | ||
1461 | |||
1462 | static int snd_opti93x_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1463 | { | ||
1464 | opti93x_t *chip = snd_kcontrol_chip(kcontrol); | ||
1465 | unsigned long flags; | ||
1466 | int reg = kcontrol->private_value & 0xff; | ||
1467 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1468 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1469 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1470 | |||
1471 | spin_lock_irqsave(&chip->lock, flags); | ||
1472 | ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; | ||
1473 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1474 | if (invert) | ||
1475 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1479 | static int snd_opti93x_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1480 | { | ||
1481 | opti93x_t *chip = snd_kcontrol_chip(kcontrol); | ||
1482 | unsigned long flags; | ||
1483 | int reg = kcontrol->private_value & 0xff; | ||
1484 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
1485 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
1486 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
1487 | int change; | ||
1488 | unsigned short val; | ||
1489 | |||
1490 | val = (ucontrol->value.integer.value[0] & mask); | ||
1491 | if (invert) | ||
1492 | val = mask - val; | ||
1493 | val <<= shift; | ||
1494 | spin_lock_irqsave(&chip->lock, flags); | ||
1495 | val = (chip->image[reg] & ~(mask << shift)) | val; | ||
1496 | change = val != chip->image[reg]; | ||
1497 | snd_opti93x_out(chip, reg, val); | ||
1498 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1499 | return change; | ||
1500 | } | ||
1501 | |||
1502 | #endif /* single */ | ||
1503 | |||
1504 | #define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ | ||
1505 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
1506 | .info = snd_opti93x_info_double, \ | ||
1507 | .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \ | ||
1508 | .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } | ||
1509 | |||
1510 | #define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \ | ||
1511 | do { xctl.private_value ^= 22; } while (0) | ||
1512 | #define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \ | ||
1513 | do { xctl.private_value &= ~0x0000ffff; \ | ||
1514 | xctl.private_value |= left_reg | (right_reg << 8); } while (0) | ||
1515 | |||
1516 | static int snd_opti93x_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1517 | { | ||
1518 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1519 | |||
1520 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1521 | uinfo->count = 2; | ||
1522 | uinfo->value.integer.min = 0; | ||
1523 | uinfo->value.integer.max = mask; | ||
1524 | return 0; | ||
1525 | } | ||
1526 | |||
1527 | static int snd_opti93x_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1528 | { | ||
1529 | opti93x_t *chip = snd_kcontrol_chip(kcontrol); | ||
1530 | unsigned long flags; | ||
1531 | int left_reg = kcontrol->private_value & 0xff; | ||
1532 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1533 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1534 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1535 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1536 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1537 | |||
1538 | spin_lock_irqsave(&chip->lock, flags); | ||
1539 | ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; | ||
1540 | ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; | ||
1541 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1542 | if (invert) { | ||
1543 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
1544 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
1545 | } | ||
1546 | return 0; | ||
1547 | } | ||
1548 | |||
1549 | static int snd_opti93x_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1550 | { | ||
1551 | opti93x_t *chip = snd_kcontrol_chip(kcontrol); | ||
1552 | unsigned long flags; | ||
1553 | int left_reg = kcontrol->private_value & 0xff; | ||
1554 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
1555 | int shift_left = (kcontrol->private_value >> 16) & 0x07; | ||
1556 | int shift_right = (kcontrol->private_value >> 19) & 0x07; | ||
1557 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
1558 | int invert = (kcontrol->private_value >> 22) & 1; | ||
1559 | int change; | ||
1560 | unsigned short val1, val2; | ||
1561 | |||
1562 | val1 = ucontrol->value.integer.value[0] & mask; | ||
1563 | val2 = ucontrol->value.integer.value[1] & mask; | ||
1564 | if (invert) { | ||
1565 | val1 = mask - val1; | ||
1566 | val2 = mask - val2; | ||
1567 | } | ||
1568 | val1 <<= shift_left; | ||
1569 | val2 <<= shift_right; | ||
1570 | spin_lock_irqsave(&chip->lock, flags); | ||
1571 | val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; | ||
1572 | val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; | ||
1573 | change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; | ||
1574 | snd_opti93x_out_image(chip, left_reg, val1); | ||
1575 | snd_opti93x_out_image(chip, right_reg, val2); | ||
1576 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1577 | return change; | ||
1578 | } | ||
1579 | |||
1580 | static snd_kcontrol_new_t snd_opti93x_controls[] = { | ||
1581 | OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), | ||
1582 | OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), | ||
1583 | OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1), | ||
1584 | OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1), | ||
1585 | OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1), | ||
1586 | OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1), | ||
1587 | OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1), | ||
1588 | OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1), | ||
1589 | OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), | ||
1590 | OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), | ||
1591 | OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1), | ||
1592 | OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1), | ||
1593 | OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1), | ||
1594 | OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), | ||
1595 | OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), | ||
1596 | OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0), | ||
1597 | { | ||
1598 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1599 | .name = "Capture Source", | ||
1600 | .info = snd_opti93x_info_mux, | ||
1601 | .get = snd_opti93x_get_mux, | ||
1602 | .put = snd_opti93x_put_mux, | ||
1603 | } | ||
1604 | }; | ||
1605 | |||
1606 | static int snd_opti93x_mixer(opti93x_t *chip) | ||
1607 | { | ||
1608 | snd_card_t *card; | ||
1609 | snd_kcontrol_new_t knew; | ||
1610 | int err; | ||
1611 | unsigned int idx; | ||
1612 | |||
1613 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
1614 | |||
1615 | card = chip->card; | ||
1616 | |||
1617 | strcpy(card->mixername, snd_opti93x_chip_id(chip)); | ||
1618 | |||
1619 | for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { | ||
1620 | knew = snd_opti93x_controls[idx]; | ||
1621 | if (chip->hardware == OPTi9XX_HW_82C930) { | ||
1622 | if (strstr(knew.name, "FM")) /* skip FM controls */ | ||
1623 | continue; | ||
1624 | else if (strcmp(knew.name, "Mic Playback Volume")) | ||
1625 | OPTi93X_DOUBLE_INVERT_INVERT(knew); | ||
1626 | else if (strstr(knew.name, "Aux")) | ||
1627 | OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT); | ||
1628 | else if (strcmp(knew.name, "PCM Playback Volume")) | ||
1629 | OPTi93X_DOUBLE_INVERT_INVERT(knew); | ||
1630 | else if (strcmp(knew.name, "Master Playback Volume")) | ||
1631 | OPTi93X_DOUBLE_INVERT_INVERT(knew); | ||
1632 | } | ||
1633 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0) | ||
1634 | return err; | ||
1635 | } | ||
1636 | return 0; | ||
1637 | } | ||
1638 | |||
1639 | #endif /* OPTi93X */ | ||
1640 | |||
1641 | static int __devinit snd_card_opti9xx_detect(snd_card_t *card, opti9xx_t *chip) | ||
1642 | { | ||
1643 | int i, err; | ||
1644 | |||
1645 | #ifndef OPTi93X | ||
1646 | for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { | ||
1647 | unsigned char value; | ||
1648 | |||
1649 | if ((err = snd_opti9xx_init(chip, i)) < 0) | ||
1650 | return err; | ||
1651 | |||
1652 | if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) | ||
1653 | continue; | ||
1654 | |||
1655 | value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); | ||
1656 | if ((value != 0xff) && (value != inb(chip->mc_base + 1))) | ||
1657 | if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) | ||
1658 | return 1; | ||
1659 | |||
1660 | release_resource(chip->res_mc_base); | ||
1661 | kfree_nocheck(chip->res_mc_base); | ||
1662 | chip->res_mc_base = NULL; | ||
1663 | |||
1664 | } | ||
1665 | #else /* OPTi93X */ | ||
1666 | for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { | ||
1667 | unsigned long flags; | ||
1668 | unsigned char value; | ||
1669 | |||
1670 | if ((err = snd_opti9xx_init(chip, i)) < 0) | ||
1671 | return err; | ||
1672 | |||
1673 | if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) | ||
1674 | continue; | ||
1675 | |||
1676 | spin_lock_irqsave(&chip->lock, flags); | ||
1677 | outb(chip->password, chip->mc_base + chip->pwd_reg); | ||
1678 | outb(((chip->mc_indir_index & (1 << 8)) >> 4) | | ||
1679 | ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base); | ||
1680 | spin_unlock_irqrestore(&chip->lock, flags); | ||
1681 | |||
1682 | value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); | ||
1683 | snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); | ||
1684 | if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) | ||
1685 | return 1; | ||
1686 | |||
1687 | release_resource(chip->res_mc_base); | ||
1688 | kfree_nocheck(chip->res_mc_base); | ||
1689 | chip->res_mc_base = NULL; | ||
1690 | } | ||
1691 | #endif /* OPTi93X */ | ||
1692 | |||
1693 | return -ENODEV; | ||
1694 | } | ||
1695 | |||
1696 | #ifdef CONFIG_PNP | ||
1697 | static int __devinit snd_card_opti9xx_pnp(opti9xx_t *chip, struct pnp_card_link *card, | ||
1698 | const struct pnp_card_device_id *pid) | ||
1699 | { | ||
1700 | struct pnp_dev *pdev; | ||
1701 | struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); | ||
1702 | int err; | ||
1703 | |||
1704 | chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); | ||
1705 | if (chip->dev == NULL) { | ||
1706 | kfree(cfg); | ||
1707 | return -EBUSY; | ||
1708 | } | ||
1709 | chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); | ||
1710 | |||
1711 | pdev = chip->dev; | ||
1712 | pnp_init_resource_table(cfg); | ||
1713 | |||
1714 | #ifdef OPTi93X | ||
1715 | if (port != SNDRV_AUTO_PORT) | ||
1716 | pnp_resource_change(&cfg->port_resource[0], port + 4, 4); | ||
1717 | #else | ||
1718 | if (pid->driver_data != 0x0924 && port != SNDRV_AUTO_PORT) | ||
1719 | pnp_resource_change(&cfg->port_resource[1], port, 4); | ||
1720 | #endif /* OPTi93X */ | ||
1721 | if (irq != SNDRV_AUTO_IRQ) | ||
1722 | pnp_resource_change(&cfg->irq_resource[0], irq, 1); | ||
1723 | if (dma1 != SNDRV_AUTO_DMA) | ||
1724 | pnp_resource_change(&cfg->dma_resource[0], dma1, 1); | ||
1725 | #if defined(CS4231) || defined(OPTi93X) | ||
1726 | if (dma2 != SNDRV_AUTO_DMA) | ||
1727 | pnp_resource_change(&cfg->dma_resource[1], dma2, 1); | ||
1728 | #else | ||
1729 | #ifdef snd_opti9xx_fixup_dma2 | ||
1730 | snd_opti9xx_fixup_dma2(pdev); | ||
1731 | #endif | ||
1732 | #endif /* CS4231 || OPTi93X */ | ||
1733 | #ifdef OPTi93X | ||
1734 | if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) | ||
1735 | pnp_resource_change(&cfg->port_resource[1], fm_port, 4); | ||
1736 | #else | ||
1737 | if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) | ||
1738 | pnp_resource_change(&cfg->port_resource[2], fm_port, 4); | ||
1739 | #endif | ||
1740 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
1741 | snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n"); | ||
1742 | err = pnp_activate_dev(pdev); | ||
1743 | if (err < 0) { | ||
1744 | snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); | ||
1745 | kfree(cfg); | ||
1746 | return err; | ||
1747 | } | ||
1748 | |||
1749 | #ifdef OPTi93X | ||
1750 | port = pnp_port_start(pdev, 0) - 4; | ||
1751 | fm_port = pnp_port_start(pdev, 1); | ||
1752 | #else | ||
1753 | if (pid->driver_data != 0x0924) | ||
1754 | port = pnp_port_start(pdev, 1); | ||
1755 | fm_port = pnp_port_start(pdev, 2); | ||
1756 | #endif /* OPTi93X */ | ||
1757 | irq = pnp_irq(pdev, 0); | ||
1758 | dma1 = pnp_dma(pdev, 0); | ||
1759 | #if defined(CS4231) || defined(OPTi93X) | ||
1760 | dma2 = pnp_dma(pdev, 1); | ||
1761 | #endif /* CS4231 || OPTi93X */ | ||
1762 | |||
1763 | pdev = chip->devmpu; | ||
1764 | if (pdev && mpu_port > 0) { | ||
1765 | pnp_init_resource_table(cfg); | ||
1766 | |||
1767 | if (mpu_port != SNDRV_AUTO_PORT) | ||
1768 | pnp_resource_change(&cfg->port_resource[0], mpu_port, 2); | ||
1769 | if (mpu_irq != SNDRV_AUTO_IRQ) | ||
1770 | pnp_resource_change(&cfg->irq_resource[0], mpu_irq, 1); | ||
1771 | |||
1772 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
1773 | snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n"); | ||
1774 | err = pnp_activate_dev(pdev); | ||
1775 | if (err < 0) { | ||
1776 | snd_printk(KERN_ERR "AUDIO pnp configure failure\n"); | ||
1777 | mpu_port = -1; | ||
1778 | chip->devmpu = NULL; | ||
1779 | } else { | ||
1780 | mpu_port = pnp_port_start(pdev, 0); | ||
1781 | mpu_irq = pnp_irq(pdev, 0); | ||
1782 | } | ||
1783 | } | ||
1784 | kfree(cfg); | ||
1785 | return pid->driver_data; | ||
1786 | } | ||
1787 | #endif /* CONFIG_PNP */ | ||
1788 | |||
1789 | #if 0 | ||
1790 | static int __devinit snd_card_opti9xx_resources(struct snd_card_opti9xx *chip, | ||
1791 | snd_card_t *card) | ||
1792 | { | ||
1793 | int error, i, pnp = 0; | ||
1794 | |||
1795 | #ifdef CONFIG_PNP | ||
1796 | pnp = chip->dev != NULL; | ||
1797 | #endif /* CONFIG_PNP */ | ||
1798 | |||
1799 | #ifndef OPTi93X | ||
1800 | if (chip->chip->hardware == OPTi9XX_HW_82C928) | ||
1801 | mpu_port = -1; | ||
1802 | #endif /* OPTi93X */ | ||
1803 | error = 0; | ||
1804 | if (!pnp && (mpu_port == SNDRV_DEFAULT_PORT1)) { | ||
1805 | for (i = 0; possible_mpu_ports[i] != -1; i++) | ||
1806 | if (!snd_register_ioport(card, possible_mpu_ports[i], 2, | ||
1807 | DRIVER_NAME" - MPU-401", NULL)) { | ||
1808 | mpu_port = possible_mpu_ports[i]; | ||
1809 | break; | ||
1810 | } | ||
1811 | if (mpu_port == SNDRV_DEFAULT_PORT1) | ||
1812 | error = -EBUSY; | ||
1813 | } | ||
1814 | else | ||
1815 | error = (mpu_port == -1) ? -ENODEV : | ||
1816 | snd_register_ioport(card, mpu_port, 2, | ||
1817 | DRIVER_NAME" - MPU-401", NULL); | ||
1818 | if (error) | ||
1819 | chip->chip->mpu_port = -1; | ||
1820 | else if (pnp && (irq == mpu_irq)) | ||
1821 | chip->chip->mpu_irq = mpu_irq; | ||
1822 | else if (!snd_register_interrupt(card, | ||
1823 | DRIVER_NAME" - MPU-401", | ||
1824 | mpu_irq, SNDRV_IRQ_TYPE_ISA, | ||
1825 | snd_card_opti9xx_mpu_interrupt, chip, | ||
1826 | pnp ? no_alternatives : possible_mpu_irqs, | ||
1827 | &chip->mpuirqptr)) { | ||
1828 | chip->chip->mpu_port = mpu_port; | ||
1829 | chip->chip->mpu_irq = chip->mpuirqptr->irq; | ||
1830 | } | ||
1831 | else | ||
1832 | chip->chip->mpu_port = -1; | ||
1833 | |||
1834 | if (!pnp && (port == SNDRV_DEFAULT_PORT1)) { | ||
1835 | for (i = 0; possible_ports[i] != -1; i++) | ||
1836 | if (!snd_register_ioport(card, possible_ports[i], 8, | ||
1837 | DRIVER_NAME" - WSS", NULL)) { | ||
1838 | port = possible_ports[i]; | ||
1839 | break; | ||
1840 | } | ||
1841 | if (port == SNDRV_DEFAULT_PORT1) | ||
1842 | return -EBUSY; | ||
1843 | } | ||
1844 | else if ((error = snd_register_ioport(card, port, 8, | ||
1845 | DRIVER_NAME" - WSS", NULL)) < 0) | ||
1846 | return error; | ||
1847 | chip->chip->wss_base = port; | ||
1848 | if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS", | ||
1849 | irq, SNDRV_IRQ_TYPE_ISA, | ||
1850 | snd_card_opti9xx_interrupt, chip, | ||
1851 | pnp ? no_alternatives : possible_irqs, | ||
1852 | &chip->irqptr)) < 0) | ||
1853 | return error; | ||
1854 | chip->chip->irq = chip->irqptr->irq; | ||
1855 | if ((error = snd_register_dma_channel(card, | ||
1856 | #if defined(CS4231) || defined(OPTi93X) | ||
1857 | DRIVER_NAME" - WSS playback", | ||
1858 | #else | ||
1859 | DRIVER_NAME" - WSS", | ||
1860 | #endif /* CS4231 || OPTi93X */ | ||
1861 | dma1, SNDRV_DMA_TYPE_ISA, dma1_size, | ||
1862 | pnp ? no_alternatives : possible_dma1s, | ||
1863 | &chip->dma1ptr)) < 0) | ||
1864 | return error; | ||
1865 | chip->chip->dma1 = chip->dma1ptr->dma; | ||
1866 | #if defined(CS4231) || defined(OPTi93X) | ||
1867 | if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture", | ||
1868 | dma2, SNDRV_DMA_TYPE_ISA, dma2_size, | ||
1869 | pnp ? no_alternatives : | ||
1870 | possible_dma2s[chip->dma1ptr->dma], | ||
1871 | &chip->dma2ptr)) < 0) | ||
1872 | return error; | ||
1873 | chip->chip->dma2 = chip->dma2ptr->dma; | ||
1874 | #endif /* CS4231 || OPTi93X */ | ||
1875 | |||
1876 | if (snd_register_ioport(card, | ||
1877 | pnp ? fm_port : fm_port = 0x388, 4, | ||
1878 | DRIVER_NAME" - OPL", NULL) < 0) | ||
1879 | fm_port = -1; | ||
1880 | chip->chip->fm_port = fm_port; | ||
1881 | |||
1882 | return 0; | ||
1883 | } | ||
1884 | #endif | ||
1885 | |||
1886 | static void snd_card_opti9xx_free(snd_card_t *card) | ||
1887 | { | ||
1888 | opti9xx_t *chip = (opti9xx_t *)card->private_data; | ||
1889 | |||
1890 | if (chip) { | ||
1891 | if (chip->res_mc_base) { | ||
1892 | release_resource(chip->res_mc_base); | ||
1893 | kfree_nocheck(chip->res_mc_base); | ||
1894 | } | ||
1895 | } | ||
1896 | } | ||
1897 | |||
1898 | static int __devinit snd_card_opti9xx_probe(struct pnp_card_link *pcard, | ||
1899 | const struct pnp_card_device_id *pid) | ||
1900 | { | ||
1901 | static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; | ||
1902 | static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; | ||
1903 | #ifdef OPTi93X | ||
1904 | static int possible_irqs[] = {5, 9, 10, 11, 7, -1}; | ||
1905 | #else | ||
1906 | static int possible_irqs[] = {9, 10, 11, 7, -1}; | ||
1907 | #endif /* OPTi93X */ | ||
1908 | static int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; | ||
1909 | static int possible_dma1s[] = {3, 1, 0, -1}; | ||
1910 | #if defined(CS4231) || defined(OPTi93X) | ||
1911 | static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; | ||
1912 | #endif /* CS4231 || OPTi93X */ | ||
1913 | int error; | ||
1914 | opti9xx_t *chip; | ||
1915 | #if defined(OPTi93X) | ||
1916 | opti93x_t *codec; | ||
1917 | #elif defined(CS4231) | ||
1918 | cs4231_t *codec; | ||
1919 | snd_timer_t *timer; | ||
1920 | #else | ||
1921 | ad1848_t *codec; | ||
1922 | #endif | ||
1923 | snd_card_t *card; | ||
1924 | snd_pcm_t *pcm; | ||
1925 | snd_rawmidi_t *rmidi; | ||
1926 | snd_hwdep_t *synth; | ||
1927 | #ifdef CONFIG_PNP | ||
1928 | int hw; | ||
1929 | #endif /* CONFIG_PNP */ | ||
1930 | |||
1931 | if (pcard && !snd_opti9xx_first_hit) | ||
1932 | return -EBUSY; | ||
1933 | if (!(card = snd_card_new(index, id, THIS_MODULE, | ||
1934 | sizeof(opti9xx_t)))) | ||
1935 | return -ENOMEM; | ||
1936 | card->private_free = snd_card_opti9xx_free; | ||
1937 | chip = (opti9xx_t *)card->private_data; | ||
1938 | |||
1939 | #ifdef CONFIG_PNP | ||
1940 | if (isapnp && pcard && (hw = snd_card_opti9xx_pnp(chip, pcard, pid)) > 0) { | ||
1941 | switch (hw) { | ||
1942 | case 0x0924: | ||
1943 | hw = OPTi9XX_HW_82C924; | ||
1944 | break; | ||
1945 | case 0x0925: | ||
1946 | hw = OPTi9XX_HW_82C925; | ||
1947 | break; | ||
1948 | case 0x0931: | ||
1949 | hw = OPTi9XX_HW_82C931; | ||
1950 | break; | ||
1951 | default: | ||
1952 | snd_card_free(card); | ||
1953 | return -ENODEV; | ||
1954 | } | ||
1955 | |||
1956 | if ((error = snd_opti9xx_init(chip, hw))) { | ||
1957 | snd_card_free(card); | ||
1958 | return error; | ||
1959 | } | ||
1960 | if (hw <= OPTi9XX_HW_82C930) | ||
1961 | chip->mc_base -= 0x80; | ||
1962 | snd_card_set_dev(card, &pcard->card->dev); | ||
1963 | } else { | ||
1964 | #endif /* CONFIG_PNP */ | ||
1965 | if ((error = snd_card_opti9xx_detect(card, chip)) < 0) { | ||
1966 | snd_card_free(card); | ||
1967 | return error; | ||
1968 | } | ||
1969 | #ifdef CONFIG_PNP | ||
1970 | } | ||
1971 | #endif /* CONFIG_PNP */ | ||
1972 | |||
1973 | if (! chip->res_mc_base && | ||
1974 | (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { | ||
1975 | snd_card_free(card); | ||
1976 | return -ENOMEM; | ||
1977 | } | ||
1978 | |||
1979 | chip->wss_base = port; | ||
1980 | chip->fm_port = fm_port; | ||
1981 | chip->mpu_port = mpu_port; | ||
1982 | chip->irq = irq; | ||
1983 | chip->mpu_irq = mpu_irq; | ||
1984 | chip->dma1 = dma1; | ||
1985 | #if defined(CS4231) || defined(OPTi93X) | ||
1986 | chip->dma2 = dma2; | ||
1987 | #endif | ||
1988 | |||
1989 | if (chip->wss_base == SNDRV_AUTO_PORT) { | ||
1990 | if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) { | ||
1991 | snd_card_free(card); | ||
1992 | snd_printk("unable to find a free WSS port\n"); | ||
1993 | return -EBUSY; | ||
1994 | } | ||
1995 | } | ||
1996 | #ifdef CONFIG_PNP | ||
1997 | if (!isapnp) { | ||
1998 | #endif | ||
1999 | if (chip->mpu_port == SNDRV_AUTO_PORT) { | ||
2000 | if ((chip->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { | ||
2001 | snd_card_free(card); | ||
2002 | snd_printk("unable to find a free MPU401 port\n"); | ||
2003 | return -EBUSY; | ||
2004 | } | ||
2005 | } | ||
2006 | if (chip->irq == SNDRV_AUTO_IRQ) { | ||
2007 | if ((chip->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
2008 | snd_card_free(card); | ||
2009 | snd_printk("unable to find a free IRQ\n"); | ||
2010 | return -EBUSY; | ||
2011 | } | ||
2012 | } | ||
2013 | if (chip->mpu_irq == SNDRV_AUTO_IRQ) { | ||
2014 | if ((chip->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { | ||
2015 | snd_card_free(card); | ||
2016 | snd_printk("unable to find a free MPU401 IRQ\n"); | ||
2017 | return -EBUSY; | ||
2018 | } | ||
2019 | } | ||
2020 | if (chip->dma1 == SNDRV_AUTO_DMA) { | ||
2021 | if ((chip->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { | ||
2022 | snd_card_free(card); | ||
2023 | snd_printk("unable to find a free DMA1\n"); | ||
2024 | return -EBUSY; | ||
2025 | } | ||
2026 | } | ||
2027 | #if defined(CS4231) || defined(OPTi93X) | ||
2028 | if (chip->dma2 == SNDRV_AUTO_DMA) { | ||
2029 | if ((chip->dma2 = snd_legacy_find_free_dma(possible_dma2s[chip->dma1 % 4])) < 0) { | ||
2030 | snd_card_free(card); | ||
2031 | snd_printk("unable to find a free DMA2\n"); | ||
2032 | return -EBUSY; | ||
2033 | } | ||
2034 | } | ||
2035 | #endif | ||
2036 | |||
2037 | #ifdef CONFIG_PNP | ||
2038 | } | ||
2039 | #endif | ||
2040 | |||
2041 | if ((error = snd_opti9xx_configure(chip))) { | ||
2042 | snd_card_free(card); | ||
2043 | return error; | ||
2044 | } | ||
2045 | |||
2046 | #if defined(OPTi93X) | ||
2047 | if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) { | ||
2048 | snd_card_free(card); | ||
2049 | return error; | ||
2050 | } | ||
2051 | if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) { | ||
2052 | snd_card_free(card); | ||
2053 | return error; | ||
2054 | } | ||
2055 | if ((error = snd_opti93x_mixer(codec)) < 0) { | ||
2056 | snd_card_free(card); | ||
2057 | return error; | ||
2058 | } | ||
2059 | #elif defined(CS4231) | ||
2060 | if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1, | ||
2061 | chip->irq, chip->dma1, chip->dma2, | ||
2062 | CS4231_HW_DETECT, | ||
2063 | 0, | ||
2064 | &codec)) < 0) { | ||
2065 | snd_card_free(card); | ||
2066 | return error; | ||
2067 | } | ||
2068 | if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) { | ||
2069 | snd_card_free(card); | ||
2070 | return error; | ||
2071 | } | ||
2072 | if ((error = snd_cs4231_mixer(codec)) < 0) { | ||
2073 | snd_card_free(card); | ||
2074 | return error; | ||
2075 | } | ||
2076 | if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) { | ||
2077 | snd_card_free(card); | ||
2078 | return error; | ||
2079 | } | ||
2080 | #else | ||
2081 | if ((error = snd_ad1848_create(card, chip->wss_base + 4, | ||
2082 | chip->irq, chip->dma1, | ||
2083 | AD1848_HW_DETECT, &codec)) < 0) { | ||
2084 | snd_card_free(card); | ||
2085 | return error; | ||
2086 | } | ||
2087 | if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) { | ||
2088 | snd_card_free(card); | ||
2089 | return error; | ||
2090 | } | ||
2091 | if ((error = snd_ad1848_mixer(codec)) < 0) { | ||
2092 | snd_card_free(card); | ||
2093 | return error; | ||
2094 | } | ||
2095 | #endif | ||
2096 | strcpy(card->driver, chip->name); | ||
2097 | sprintf(card->shortname, "OPTi %s", card->driver); | ||
2098 | #if defined(CS4231) || defined(OPTi93X) | ||
2099 | sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", | ||
2100 | card->shortname, pcm->name, chip->wss_base + 4, | ||
2101 | chip->irq, chip->dma1, chip->dma2); | ||
2102 | #else | ||
2103 | sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d", | ||
2104 | card->shortname, pcm->name, chip->wss_base + 4, | ||
2105 | chip->irq, chip->dma1); | ||
2106 | #endif /* CS4231 || OPTi93X */ | ||
2107 | |||
2108 | if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT) | ||
2109 | rmidi = NULL; | ||
2110 | else | ||
2111 | if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, | ||
2112 | chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT, | ||
2113 | &rmidi))) | ||
2114 | snd_printk("no MPU-401 device at 0x%lx?\n", chip->mpu_port); | ||
2115 | |||
2116 | if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { | ||
2117 | opl3_t *opl3 = NULL; | ||
2118 | #ifndef OPTi93X | ||
2119 | if (chip->hardware == OPTi9XX_HW_82C928 || | ||
2120 | chip->hardware == OPTi9XX_HW_82C929 || | ||
2121 | chip->hardware == OPTi9XX_HW_82C924) { | ||
2122 | opl4_t *opl4; | ||
2123 | /* assume we have an OPL4 */ | ||
2124 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), | ||
2125 | 0x20, 0x20); | ||
2126 | if (snd_opl4_create(card, | ||
2127 | chip->fm_port, | ||
2128 | chip->fm_port - 8, | ||
2129 | 2, &opl3, &opl4) < 0) { | ||
2130 | /* no luck, use OPL3 instead */ | ||
2131 | snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), | ||
2132 | 0x00, 0x20); | ||
2133 | } | ||
2134 | } | ||
2135 | #endif /* !OPTi93X */ | ||
2136 | if (!opl3 && snd_opl3_create(card, | ||
2137 | chip->fm_port, | ||
2138 | chip->fm_port + 2, | ||
2139 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
2140 | snd_printk("no OPL device at 0x%lx-0x%lx\n", | ||
2141 | chip->fm_port, chip->fm_port + 4 - 1); | ||
2142 | } | ||
2143 | if (opl3) { | ||
2144 | if ((error = snd_opl3_timer_new(opl3, | ||
2145 | #ifdef CS4231 | ||
2146 | 1, 2)) < 0) { | ||
2147 | #else | ||
2148 | 0, 1)) < 0) { | ||
2149 | #endif /* CS4231 */ | ||
2150 | snd_card_free(card); | ||
2151 | return error; | ||
2152 | } | ||
2153 | if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) { | ||
2154 | snd_card_free(card); | ||
2155 | return error; | ||
2156 | } | ||
2157 | } | ||
2158 | } | ||
2159 | |||
2160 | if ((error = snd_card_register(card))) { | ||
2161 | snd_card_free(card); | ||
2162 | return error; | ||
2163 | } | ||
2164 | snd_opti9xx_first_hit = 0; | ||
2165 | if (pcard) | ||
2166 | pnp_set_card_drvdata(pcard, card); | ||
2167 | else | ||
2168 | snd_opti9xx_legacy = card; | ||
2169 | return 0; | ||
2170 | } | ||
2171 | |||
2172 | #ifdef CONFIG_PNP | ||
2173 | static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard) | ||
2174 | { | ||
2175 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
2176 | |||
2177 | snd_card_disconnect(card); | ||
2178 | snd_card_free_in_thread(card); | ||
2179 | snd_opti9xx_first_hit = 0; | ||
2180 | } | ||
2181 | |||
2182 | static struct pnp_card_driver opti9xx_pnpc_driver = { | ||
2183 | .flags = PNP_DRIVER_RES_DISABLE, | ||
2184 | .name = "opti9xx", | ||
2185 | .id_table = snd_opti9xx_pnpids, | ||
2186 | .probe = snd_card_opti9xx_probe, | ||
2187 | .remove = __devexit_p(snd_opti9xx_pnp_remove), | ||
2188 | }; | ||
2189 | #endif | ||
2190 | |||
2191 | static int __init alsa_card_opti9xx_init(void) | ||
2192 | { | ||
2193 | int cards, error; | ||
2194 | |||
2195 | #ifdef CONFIG_PNP | ||
2196 | cards = pnp_register_card_driver(&opti9xx_pnpc_driver); | ||
2197 | #else | ||
2198 | cards = 0; | ||
2199 | #endif | ||
2200 | if (cards == 0 && (error = snd_card_opti9xx_probe(NULL, NULL)) < 0) { | ||
2201 | #ifdef CONFIG_PNP | ||
2202 | pnp_unregister_card_driver(&opti9xx_pnpc_driver); | ||
2203 | #endif | ||
2204 | #ifdef MODULE | ||
2205 | #ifdef OPTi93X | ||
2206 | printk(KERN_ERR "no OPTi 82C93x soundcard found\n"); | ||
2207 | #else | ||
2208 | printk(KERN_ERR "no OPTi 82C92x soundcard found\n"); | ||
2209 | #endif /* OPTi93X */ | ||
2210 | #endif | ||
2211 | return error; | ||
2212 | } | ||
2213 | return 0; | ||
2214 | } | ||
2215 | |||
2216 | static void __exit alsa_card_opti9xx_exit(void) | ||
2217 | { | ||
2218 | #ifdef CONFIG_PNP | ||
2219 | pnp_unregister_card_driver(&opti9xx_pnpc_driver); | ||
2220 | #endif | ||
2221 | if (snd_opti9xx_legacy) | ||
2222 | snd_card_free(snd_opti9xx_legacy); | ||
2223 | } | ||
2224 | |||
2225 | module_init(alsa_card_opti9xx_init) | ||
2226 | module_exit(alsa_card_opti9xx_exit) | ||
diff --git a/sound/isa/opti9xx/opti92x-cs4231.c b/sound/isa/opti9xx/opti92x-cs4231.c new file mode 100644 index 000000000000..b17ab19f6d81 --- /dev/null +++ b/sound/isa/opti9xx/opti92x-cs4231.c | |||
@@ -0,0 +1,2 @@ | |||
1 | #define CS4231 | ||
2 | #include "opti92x-ad1848.c" | ||
diff --git a/sound/isa/opti9xx/opti93x.c b/sound/isa/opti9xx/opti93x.c new file mode 100644 index 000000000000..bad9da521bf9 --- /dev/null +++ b/sound/isa/opti9xx/opti93x.c | |||
@@ -0,0 +1,3 @@ | |||
1 | #define OPTi93X | ||
2 | #include "opti92x-ad1848.c" | ||
3 | |||
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile new file mode 100644 index 000000000000..fd9d9c5726fc --- /dev/null +++ b/sound/isa/sb/Makefile | |||
@@ -0,0 +1,39 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-sb-common-objs := sb_common.o sb_mixer.o | ||
7 | snd-sb8-dsp-objs := sb8_main.o sb8_midi.o | ||
8 | snd-sb16-dsp-objs := sb16_main.o | ||
9 | snd-sb16-csp-objs := sb16_csp.o | ||
10 | snd-sb8-objs := sb8.o | ||
11 | snd-sb16-objs := sb16.o | ||
12 | snd-sbawe-objs := sbawe.o emu8000.o | ||
13 | snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o | ||
14 | snd-es968-objs := es968.o | ||
15 | |||
16 | # | ||
17 | # this function returns: | ||
18 | # "m" - CONFIG_SND_SEQUENCER is m | ||
19 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
20 | # otherwise parameter #1 value | ||
21 | # | ||
22 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
23 | |||
24 | # Toplevel Module Dependency | ||
25 | obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o | ||
26 | obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o | ||
27 | obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o | ||
28 | obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o | ||
29 | obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o | ||
30 | obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o | ||
31 | obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o | ||
32 | obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o | ||
33 | ifeq ($(CONFIG_SND_SB16_CSP),y) | ||
34 | obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o | ||
35 | obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o | ||
36 | endif | ||
37 | obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o | ||
38 | |||
39 | obj-m := $(sort $(obj-m)) | ||
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c new file mode 100644 index 000000000000..028af4066595 --- /dev/null +++ b/sound/isa/sb/emu8000.c | |||
@@ -0,0 +1,1170 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk> | ||
4 | * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * Routines for control of EMU8000 chip | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/emu8000.h> | ||
31 | #include <sound/emu8000_reg.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/uaccess.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <sound/control.h> | ||
36 | #include <sound/initval.h> | ||
37 | |||
38 | /* | ||
39 | * emu8000 register controls | ||
40 | */ | ||
41 | |||
42 | /* | ||
43 | * The following routines read and write registers on the emu8000. They | ||
44 | * should always be called via the EMU8000*READ/WRITE macros and never | ||
45 | * directly. The macros handle the port number and command word. | ||
46 | */ | ||
47 | /* Write a word */ | ||
48 | void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) | ||
49 | { | ||
50 | unsigned long flags; | ||
51 | spin_lock_irqsave(&emu->reg_lock, flags); | ||
52 | if (reg != emu->last_reg) { | ||
53 | outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ | ||
54 | emu->last_reg = reg; | ||
55 | } | ||
56 | outw((unsigned short)val, port); /* Send data */ | ||
57 | spin_unlock_irqrestore(&emu->reg_lock, flags); | ||
58 | } | ||
59 | |||
60 | /* Read a word */ | ||
61 | unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg) | ||
62 | { | ||
63 | unsigned short res; | ||
64 | unsigned long flags; | ||
65 | spin_lock_irqsave(&emu->reg_lock, flags); | ||
66 | if (reg != emu->last_reg) { | ||
67 | outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ | ||
68 | emu->last_reg = reg; | ||
69 | } | ||
70 | res = inw(port); /* Read data */ | ||
71 | spin_unlock_irqrestore(&emu->reg_lock, flags); | ||
72 | return res; | ||
73 | } | ||
74 | |||
75 | /* Write a double word */ | ||
76 | void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) | ||
77 | { | ||
78 | unsigned long flags; | ||
79 | spin_lock_irqsave(&emu->reg_lock, flags); | ||
80 | if (reg != emu->last_reg) { | ||
81 | outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ | ||
82 | emu->last_reg = reg; | ||
83 | } | ||
84 | outw((unsigned short)val, port); /* Send low word of data */ | ||
85 | outw((unsigned short)(val>>16), port+2); /* Send high word of data */ | ||
86 | spin_unlock_irqrestore(&emu->reg_lock, flags); | ||
87 | } | ||
88 | |||
89 | /* Read a double word */ | ||
90 | unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg) | ||
91 | { | ||
92 | unsigned short low; | ||
93 | unsigned int res; | ||
94 | unsigned long flags; | ||
95 | spin_lock_irqsave(&emu->reg_lock, flags); | ||
96 | if (reg != emu->last_reg) { | ||
97 | outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ | ||
98 | emu->last_reg = reg; | ||
99 | } | ||
100 | low = inw(port); /* Read low word of data */ | ||
101 | res = low + (inw(port+2) << 16); | ||
102 | spin_unlock_irqrestore(&emu->reg_lock, flags); | ||
103 | return res; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Set up / close a channel to be used for DMA. | ||
108 | */ | ||
109 | /*exported*/ void | ||
110 | snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode) | ||
111 | { | ||
112 | unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0; | ||
113 | mode &= EMU8000_RAM_MODE_MASK; | ||
114 | if (mode == EMU8000_RAM_CLOSE) { | ||
115 | EMU8000_CCCA_WRITE(emu, ch, 0); | ||
116 | EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); | ||
117 | return; | ||
118 | } | ||
119 | EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); | ||
120 | EMU8000_VTFT_WRITE(emu, ch, 0); | ||
121 | EMU8000_CVCF_WRITE(emu, ch, 0); | ||
122 | EMU8000_PTRX_WRITE(emu, ch, 0x40000000); | ||
123 | EMU8000_CPF_WRITE(emu, ch, 0x40000000); | ||
124 | EMU8000_PSST_WRITE(emu, ch, 0); | ||
125 | EMU8000_CSL_WRITE(emu, ch, 0); | ||
126 | if (mode == EMU8000_RAM_WRITE) /* DMA write */ | ||
127 | EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit); | ||
128 | else /* DMA read */ | ||
129 | EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit); | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | */ | ||
134 | static void __init | ||
135 | snd_emu8000_read_wait(emu8000_t *emu) | ||
136 | { | ||
137 | while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { | ||
138 | set_current_state(TASK_INTERRUPTIBLE); | ||
139 | schedule_timeout(1); | ||
140 | if (signal_pending(current)) | ||
141 | break; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | */ | ||
147 | static void __init | ||
148 | snd_emu8000_write_wait(emu8000_t *emu) | ||
149 | { | ||
150 | while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { | ||
151 | set_current_state(TASK_INTERRUPTIBLE); | ||
152 | schedule_timeout(1); | ||
153 | if (signal_pending(current)) | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * detect a card at the given port | ||
160 | */ | ||
161 | static int __init | ||
162 | snd_emu8000_detect(emu8000_t *emu) | ||
163 | { | ||
164 | /* Initialise */ | ||
165 | EMU8000_HWCF1_WRITE(emu, 0x0059); | ||
166 | EMU8000_HWCF2_WRITE(emu, 0x0020); | ||
167 | EMU8000_HWCF3_WRITE(emu, 0x0000); | ||
168 | /* Check for a recognisable emu8000 */ | ||
169 | /* | ||
170 | if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) | ||
171 | return -ENODEV; | ||
172 | */ | ||
173 | if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) | ||
174 | return -ENODEV; | ||
175 | if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) | ||
176 | return -ENODEV; | ||
177 | |||
178 | snd_printdd("EMU8000 [0x%lx]: Synth chip found\n", | ||
179 | emu->port1); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | |||
184 | /* | ||
185 | * intiailize audio channels | ||
186 | */ | ||
187 | static void __init | ||
188 | init_audio(emu8000_t *emu) | ||
189 | { | ||
190 | int ch; | ||
191 | |||
192 | /* turn off envelope engines */ | ||
193 | for (ch = 0; ch < EMU8000_CHANNELS; ch++) | ||
194 | EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); | ||
195 | |||
196 | /* reset all other parameters to zero */ | ||
197 | for (ch = 0; ch < EMU8000_CHANNELS; ch++) { | ||
198 | EMU8000_ENVVOL_WRITE(emu, ch, 0); | ||
199 | EMU8000_ENVVAL_WRITE(emu, ch, 0); | ||
200 | EMU8000_DCYSUS_WRITE(emu, ch, 0); | ||
201 | EMU8000_ATKHLDV_WRITE(emu, ch, 0); | ||
202 | EMU8000_LFO1VAL_WRITE(emu, ch, 0); | ||
203 | EMU8000_ATKHLD_WRITE(emu, ch, 0); | ||
204 | EMU8000_LFO2VAL_WRITE(emu, ch, 0); | ||
205 | EMU8000_IP_WRITE(emu, ch, 0); | ||
206 | EMU8000_IFATN_WRITE(emu, ch, 0); | ||
207 | EMU8000_PEFE_WRITE(emu, ch, 0); | ||
208 | EMU8000_FMMOD_WRITE(emu, ch, 0); | ||
209 | EMU8000_TREMFRQ_WRITE(emu, ch, 0); | ||
210 | EMU8000_FM2FRQ2_WRITE(emu, ch, 0); | ||
211 | EMU8000_PTRX_WRITE(emu, ch, 0); | ||
212 | EMU8000_VTFT_WRITE(emu, ch, 0); | ||
213 | EMU8000_PSST_WRITE(emu, ch, 0); | ||
214 | EMU8000_CSL_WRITE(emu, ch, 0); | ||
215 | EMU8000_CCCA_WRITE(emu, ch, 0); | ||
216 | } | ||
217 | |||
218 | for (ch = 0; ch < EMU8000_CHANNELS; ch++) { | ||
219 | EMU8000_CPF_WRITE(emu, ch, 0); | ||
220 | EMU8000_CVCF_WRITE(emu, ch, 0); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | |||
225 | /* | ||
226 | * initialize DMA address | ||
227 | */ | ||
228 | static void __init | ||
229 | init_dma(emu8000_t *emu) | ||
230 | { | ||
231 | EMU8000_SMALR_WRITE(emu, 0); | ||
232 | EMU8000_SMARR_WRITE(emu, 0); | ||
233 | EMU8000_SMALW_WRITE(emu, 0); | ||
234 | EMU8000_SMARW_WRITE(emu, 0); | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * initialization arrays; from ADIP | ||
239 | */ | ||
240 | static unsigned short init1[128] /*__devinitdata*/ = { | ||
241 | 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, | ||
242 | 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, | ||
243 | 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, | ||
244 | 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, | ||
245 | |||
246 | 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, | ||
247 | 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, | ||
248 | 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, | ||
249 | 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, | ||
250 | |||
251 | 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, | ||
252 | 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, | ||
253 | 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, | ||
254 | 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, | ||
255 | |||
256 | 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, | ||
257 | 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, | ||
258 | 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, | ||
259 | 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, | ||
260 | }; | ||
261 | |||
262 | static unsigned short init2[128] /*__devinitdata*/ = { | ||
263 | 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, | ||
264 | 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, | ||
265 | 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, | ||
266 | 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, | ||
267 | |||
268 | 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, | ||
269 | 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, | ||
270 | 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, | ||
271 | 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, | ||
272 | |||
273 | 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, | ||
274 | 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, | ||
275 | 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, | ||
276 | 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, | ||
277 | |||
278 | 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, | ||
279 | 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, | ||
280 | 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, | ||
281 | 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, | ||
282 | }; | ||
283 | |||
284 | static unsigned short init3[128] /*__devinitdata*/ = { | ||
285 | 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, | ||
286 | 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, | ||
287 | 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, | ||
288 | 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, | ||
289 | |||
290 | 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, | ||
291 | 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, | ||
292 | 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, | ||
293 | 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, | ||
294 | |||
295 | 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, | ||
296 | 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, | ||
297 | 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, | ||
298 | 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, | ||
299 | |||
300 | 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, | ||
301 | 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, | ||
302 | 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, | ||
303 | 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, | ||
304 | }; | ||
305 | |||
306 | static unsigned short init4[128] /*__devinitdata*/ = { | ||
307 | 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, | ||
308 | 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, | ||
309 | 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, | ||
310 | 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, | ||
311 | |||
312 | 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, | ||
313 | 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, | ||
314 | 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, | ||
315 | 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, | ||
316 | |||
317 | 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, | ||
318 | 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, | ||
319 | 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, | ||
320 | 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, | ||
321 | |||
322 | 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, | ||
323 | 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, | ||
324 | 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, | ||
325 | 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, | ||
326 | }; | ||
327 | |||
328 | /* send an initialization array | ||
329 | * Taken from the oss driver, not obvious from the doc how this | ||
330 | * is meant to work | ||
331 | */ | ||
332 | static void __init | ||
333 | send_array(emu8000_t *emu, unsigned short *data, int size) | ||
334 | { | ||
335 | int i; | ||
336 | unsigned short *p; | ||
337 | |||
338 | p = data; | ||
339 | for (i = 0; i < size; i++, p++) | ||
340 | EMU8000_INIT1_WRITE(emu, i, *p); | ||
341 | for (i = 0; i < size; i++, p++) | ||
342 | EMU8000_INIT2_WRITE(emu, i, *p); | ||
343 | for (i = 0; i < size; i++, p++) | ||
344 | EMU8000_INIT3_WRITE(emu, i, *p); | ||
345 | for (i = 0; i < size; i++, p++) | ||
346 | EMU8000_INIT4_WRITE(emu, i, *p); | ||
347 | } | ||
348 | |||
349 | |||
350 | /* | ||
351 | * Send initialization arrays to start up, this just follows the | ||
352 | * initialisation sequence in the adip. | ||
353 | */ | ||
354 | static void __init | ||
355 | init_arrays(emu8000_t *emu) | ||
356 | { | ||
357 | send_array(emu, init1, ARRAY_SIZE(init1)/4); | ||
358 | |||
359 | msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */ | ||
360 | send_array(emu, init2, ARRAY_SIZE(init2)/4); | ||
361 | send_array(emu, init3, ARRAY_SIZE(init3)/4); | ||
362 | |||
363 | EMU8000_HWCF4_WRITE(emu, 0); | ||
364 | EMU8000_HWCF5_WRITE(emu, 0x83); | ||
365 | EMU8000_HWCF6_WRITE(emu, 0x8000); | ||
366 | |||
367 | send_array(emu, init4, ARRAY_SIZE(init4)/4); | ||
368 | } | ||
369 | |||
370 | |||
371 | #define UNIQUE_ID1 0xa5b9 | ||
372 | #define UNIQUE_ID2 0x9d53 | ||
373 | |||
374 | /* | ||
375 | * Size the onboard memory. | ||
376 | * This is written so as not to need arbitary delays after the write. It | ||
377 | * seems that the only way to do this is to use the one channel and keep | ||
378 | * reallocating between read and write. | ||
379 | */ | ||
380 | static void __init | ||
381 | size_dram(emu8000_t *emu) | ||
382 | { | ||
383 | int i, size; | ||
384 | |||
385 | if (emu->dram_checked) | ||
386 | return; | ||
387 | |||
388 | size = 0; | ||
389 | |||
390 | /* write out a magic number */ | ||
391 | snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE); | ||
392 | snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ); | ||
393 | EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET); | ||
394 | EMU8000_SMLD_WRITE(emu, UNIQUE_ID1); | ||
395 | snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */ | ||
396 | |||
397 | while (size < EMU8000_MAX_DRAM) { | ||
398 | |||
399 | size += 512 * 1024; /* increment 512kbytes */ | ||
400 | |||
401 | /* Write a unique data on the test address. | ||
402 | * if the address is out of range, the data is written on | ||
403 | * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is | ||
404 | * changed by this data. | ||
405 | */ | ||
406 | /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/ | ||
407 | EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); | ||
408 | EMU8000_SMLD_WRITE(emu, UNIQUE_ID2); | ||
409 | snd_emu8000_write_wait(emu); | ||
410 | |||
411 | /* | ||
412 | * read the data on the just written DRAM address | ||
413 | * if not the same then we have reached the end of ram. | ||
414 | */ | ||
415 | /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/ | ||
416 | EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); | ||
417 | /*snd_emu8000_read_wait(emu);*/ | ||
418 | EMU8000_SMLD_READ(emu); /* discard stale data */ | ||
419 | if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2) | ||
420 | break; /* we must have wrapped around */ | ||
421 | |||
422 | snd_emu8000_read_wait(emu); | ||
423 | |||
424 | /* | ||
425 | * If it is the same it could be that the address just | ||
426 | * wraps back to the beginning; so check to see if the | ||
427 | * initial value has been overwritten. | ||
428 | */ | ||
429 | EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); | ||
430 | EMU8000_SMLD_READ(emu); /* discard stale data */ | ||
431 | if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) | ||
432 | break; /* we must have wrapped around */ | ||
433 | snd_emu8000_read_wait(emu); | ||
434 | } | ||
435 | |||
436 | /* wait until FULL bit in SMAxW register is false */ | ||
437 | for (i = 0; i < 10000; i++) { | ||
438 | if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) | ||
439 | break; | ||
440 | set_current_state(TASK_INTERRUPTIBLE); | ||
441 | schedule_timeout(1); | ||
442 | if (signal_pending(current)) | ||
443 | break; | ||
444 | } | ||
445 | snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE); | ||
446 | snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); | ||
447 | |||
448 | snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n", | ||
449 | emu->port1, size/1024); | ||
450 | |||
451 | emu->mem_size = size; | ||
452 | emu->dram_checked = 1; | ||
453 | } | ||
454 | |||
455 | |||
456 | /* | ||
457 | * Initiailise the FM section. You have to do this to use sample RAM | ||
458 | * and therefore lose 2 voices. | ||
459 | */ | ||
460 | /*exported*/ void | ||
461 | snd_emu8000_init_fm(emu8000_t *emu) | ||
462 | { | ||
463 | unsigned long flags; | ||
464 | |||
465 | /* Initialize the last two channels for DRAM refresh and producing | ||
466 | the reverb and chorus effects for Yamaha OPL-3 synthesizer */ | ||
467 | |||
468 | /* 31: FM left channel, 0xffffe0-0xffffe8 */ | ||
469 | EMU8000_DCYSUSV_WRITE(emu, 30, 0x80); | ||
470 | EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */ | ||
471 | EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24)); | ||
472 | EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8)); | ||
473 | EMU8000_CPF_WRITE(emu, 30, 0); | ||
474 | EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3); | ||
475 | |||
476 | /* 32: FM right channel, 0xfffff0-0xfffff8 */ | ||
477 | EMU8000_DCYSUSV_WRITE(emu, 31, 0x80); | ||
478 | EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */ | ||
479 | EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24)); | ||
480 | EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8)); | ||
481 | EMU8000_CPF_WRITE(emu, 31, 0x8000); | ||
482 | EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3); | ||
483 | |||
484 | snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0); | ||
485 | |||
486 | spin_lock_irqsave(&emu->reg_lock, flags); | ||
487 | while (!(inw(EMU8000_PTR(emu)) & 0x1000)) | ||
488 | ; | ||
489 | while ((inw(EMU8000_PTR(emu)) & 0x1000)) | ||
490 | ; | ||
491 | spin_unlock_irqrestore(&emu->reg_lock, flags); | ||
492 | snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828); | ||
493 | /* this is really odd part.. */ | ||
494 | outb(0x3C, EMU8000_PTR(emu)); | ||
495 | outb(0, EMU8000_DATA1(emu)); | ||
496 | |||
497 | /* skew volume & cutoff */ | ||
498 | EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF); | ||
499 | EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF); | ||
500 | } | ||
501 | |||
502 | |||
503 | /* | ||
504 | * The main initialization routine. | ||
505 | */ | ||
506 | static void __init | ||
507 | snd_emu8000_init_hw(emu8000_t *emu) | ||
508 | { | ||
509 | int i; | ||
510 | |||
511 | emu->last_reg = 0xffff; /* reset the last register index */ | ||
512 | |||
513 | /* initialize hardware configuration */ | ||
514 | EMU8000_HWCF1_WRITE(emu, 0x0059); | ||
515 | EMU8000_HWCF2_WRITE(emu, 0x0020); | ||
516 | |||
517 | /* disable audio; this seems to reduce a clicking noise a bit.. */ | ||
518 | EMU8000_HWCF3_WRITE(emu, 0); | ||
519 | |||
520 | /* initialize audio channels */ | ||
521 | init_audio(emu); | ||
522 | |||
523 | /* initialize DMA */ | ||
524 | init_dma(emu); | ||
525 | |||
526 | /* initialize init arrays */ | ||
527 | init_arrays(emu); | ||
528 | |||
529 | /* | ||
530 | * Initialize the FM section of the AWE32, this is needed | ||
531 | * for DRAM refresh as well | ||
532 | */ | ||
533 | snd_emu8000_init_fm(emu); | ||
534 | |||
535 | /* terminate all voices */ | ||
536 | for (i = 0; i < EMU8000_DRAM_VOICES; i++) | ||
537 | EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F); | ||
538 | |||
539 | /* check DRAM memory size */ | ||
540 | size_dram(emu); | ||
541 | |||
542 | /* enable audio */ | ||
543 | EMU8000_HWCF3_WRITE(emu, 0x4); | ||
544 | |||
545 | /* set equzlier, chorus and reverb modes */ | ||
546 | snd_emu8000_update_equalizer(emu); | ||
547 | snd_emu8000_update_chorus_mode(emu); | ||
548 | snd_emu8000_update_reverb_mode(emu); | ||
549 | } | ||
550 | |||
551 | |||
552 | /*---------------------------------------------------------------- | ||
553 | * Bass/Treble Equalizer | ||
554 | *----------------------------------------------------------------*/ | ||
555 | |||
556 | static unsigned short bass_parm[12][3] = { | ||
557 | {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ | ||
558 | {0xD25B, 0xD35B, 0x0000}, /* -8 */ | ||
559 | {0xD24C, 0xD34C, 0x0000}, /* -6 */ | ||
560 | {0xD23D, 0xD33D, 0x0000}, /* -4 */ | ||
561 | {0xD21F, 0xD31F, 0x0000}, /* -2 */ | ||
562 | {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ | ||
563 | {0xC219, 0xC319, 0x0001}, /* +2 */ | ||
564 | {0xC22A, 0xC32A, 0x0001}, /* +4 */ | ||
565 | {0xC24C, 0xC34C, 0x0001}, /* +6 */ | ||
566 | {0xC26E, 0xC36E, 0x0001}, /* +8 */ | ||
567 | {0xC248, 0xC384, 0x0002}, /* +10 */ | ||
568 | {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ | ||
569 | }; | ||
570 | |||
571 | static unsigned short treble_parm[12][9] = { | ||
572 | {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ | ||
573 | {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, | ||
574 | {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, | ||
575 | {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, | ||
576 | {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, | ||
577 | {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, | ||
578 | {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, | ||
579 | {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, | ||
580 | {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, | ||
581 | {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ | ||
582 | {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, | ||
583 | {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */ | ||
584 | }; | ||
585 | |||
586 | |||
587 | /* | ||
588 | * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] | ||
589 | */ | ||
590 | /*exported*/ void | ||
591 | snd_emu8000_update_equalizer(emu8000_t *emu) | ||
592 | { | ||
593 | unsigned short w; | ||
594 | int bass = emu->bass_level; | ||
595 | int treble = emu->treble_level; | ||
596 | |||
597 | if (bass < 0 || bass > 11 || treble < 0 || treble > 11) | ||
598 | return; | ||
599 | EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]); | ||
600 | EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]); | ||
601 | EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]); | ||
602 | EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]); | ||
603 | EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]); | ||
604 | EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]); | ||
605 | EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]); | ||
606 | EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]); | ||
607 | EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]); | ||
608 | EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]); | ||
609 | w = bass_parm[bass][2] + treble_parm[treble][8]; | ||
610 | EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262)); | ||
611 | EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362)); | ||
612 | } | ||
613 | |||
614 | |||
615 | /*---------------------------------------------------------------- | ||
616 | * Chorus mode control | ||
617 | *----------------------------------------------------------------*/ | ||
618 | |||
619 | /* | ||
620 | * chorus mode parameters | ||
621 | */ | ||
622 | #define SNDRV_EMU8000_CHORUS_1 0 | ||
623 | #define SNDRV_EMU8000_CHORUS_2 1 | ||
624 | #define SNDRV_EMU8000_CHORUS_3 2 | ||
625 | #define SNDRV_EMU8000_CHORUS_4 3 | ||
626 | #define SNDRV_EMU8000_CHORUS_FEEDBACK 4 | ||
627 | #define SNDRV_EMU8000_CHORUS_FLANGER 5 | ||
628 | #define SNDRV_EMU8000_CHORUS_SHORTDELAY 6 | ||
629 | #define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7 | ||
630 | #define SNDRV_EMU8000_CHORUS_PREDEFINED 8 | ||
631 | /* user can define chorus modes up to 32 */ | ||
632 | #define SNDRV_EMU8000_CHORUS_NUMBERS 32 | ||
633 | |||
634 | typedef struct soundfont_chorus_fx_t { | ||
635 | unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ | ||
636 | unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ | ||
637 | unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ | ||
638 | unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ | ||
639 | unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ | ||
640 | } soundfont_chorus_fx_t; | ||
641 | |||
642 | /* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ | ||
643 | static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; | ||
644 | static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = { | ||
645 | {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ | ||
646 | {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ | ||
647 | {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ | ||
648 | {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ | ||
649 | {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ | ||
650 | {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ | ||
651 | {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */ | ||
652 | {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */ | ||
653 | }; | ||
654 | |||
655 | /*exported*/ int | ||
656 | snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void __user *buf, long len) | ||
657 | { | ||
658 | soundfont_chorus_fx_t rec; | ||
659 | if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) { | ||
660 | snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode); | ||
661 | return -EINVAL; | ||
662 | } | ||
663 | if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) | ||
664 | return -EFAULT; | ||
665 | chorus_parm[mode] = rec; | ||
666 | chorus_defined[mode] = 1; | ||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | /*exported*/ void | ||
671 | snd_emu8000_update_chorus_mode(emu8000_t *emu) | ||
672 | { | ||
673 | int effect = emu->chorus_mode; | ||
674 | if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS || | ||
675 | (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect])) | ||
676 | return; | ||
677 | EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback); | ||
678 | EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset); | ||
679 | EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth); | ||
680 | EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay); | ||
681 | EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq); | ||
682 | EMU8000_HWCF6_WRITE(emu, 0x8000); | ||
683 | EMU8000_HWCF7_WRITE(emu, 0x0000); | ||
684 | } | ||
685 | |||
686 | /*---------------------------------------------------------------- | ||
687 | * Reverb mode control | ||
688 | *----------------------------------------------------------------*/ | ||
689 | |||
690 | /* | ||
691 | * reverb mode parameters | ||
692 | */ | ||
693 | #define SNDRV_EMU8000_REVERB_ROOM1 0 | ||
694 | #define SNDRV_EMU8000_REVERB_ROOM2 1 | ||
695 | #define SNDRV_EMU8000_REVERB_ROOM3 2 | ||
696 | #define SNDRV_EMU8000_REVERB_HALL1 3 | ||
697 | #define SNDRV_EMU8000_REVERB_HALL2 4 | ||
698 | #define SNDRV_EMU8000_REVERB_PLATE 5 | ||
699 | #define SNDRV_EMU8000_REVERB_DELAY 6 | ||
700 | #define SNDRV_EMU8000_REVERB_PANNINGDELAY 7 | ||
701 | #define SNDRV_EMU8000_REVERB_PREDEFINED 8 | ||
702 | /* user can define reverb modes up to 32 */ | ||
703 | #define SNDRV_EMU8000_REVERB_NUMBERS 32 | ||
704 | |||
705 | typedef struct soundfont_reverb_fx_t { | ||
706 | unsigned short parms[28]; | ||
707 | } soundfont_reverb_fx_t; | ||
708 | |||
709 | /* reverb mode settings; write the following 28 data of 16 bit length | ||
710 | * on the corresponding ports in the reverb_cmds array | ||
711 | */ | ||
712 | static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; | ||
713 | static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = { | ||
714 | {{ /* room 1 */ | ||
715 | 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, | ||
716 | 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, | ||
717 | 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, | ||
718 | 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, | ||
719 | }}, | ||
720 | {{ /* room 2 */ | ||
721 | 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, | ||
722 | 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, | ||
723 | 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, | ||
724 | 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, | ||
725 | }}, | ||
726 | {{ /* room 3 */ | ||
727 | 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, | ||
728 | 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, | ||
729 | 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, | ||
730 | 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, | ||
731 | }}, | ||
732 | {{ /* hall 1 */ | ||
733 | 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, | ||
734 | 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, | ||
735 | 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, | ||
736 | 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, | ||
737 | }}, | ||
738 | {{ /* hall 2 */ | ||
739 | 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, | ||
740 | 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, | ||
741 | 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, | ||
742 | 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, | ||
743 | }}, | ||
744 | {{ /* plate */ | ||
745 | 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, | ||
746 | 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, | ||
747 | 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, | ||
748 | 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, | ||
749 | }}, | ||
750 | {{ /* delay */ | ||
751 | 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, | ||
752 | 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, | ||
753 | 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, | ||
754 | 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, | ||
755 | }}, | ||
756 | {{ /* panning delay */ | ||
757 | 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, | ||
758 | 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, | ||
759 | 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, | ||
760 | 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, | ||
761 | }}, | ||
762 | }; | ||
763 | |||
764 | enum { DATA1, DATA2 }; | ||
765 | #define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1 | ||
766 | #define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2 | ||
767 | #define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1 | ||
768 | #define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2 | ||
769 | |||
770 | static struct reverb_cmd_pair { | ||
771 | unsigned short cmd, port; | ||
772 | } reverb_cmds[28] = { | ||
773 | {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, | ||
774 | {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, | ||
775 | {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, | ||
776 | {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, | ||
777 | {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, | ||
778 | {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, | ||
779 | {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, | ||
780 | }; | ||
781 | |||
782 | /*exported*/ int | ||
783 | snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void __user *buf, long len) | ||
784 | { | ||
785 | soundfont_reverb_fx_t rec; | ||
786 | |||
787 | if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) { | ||
788 | snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) | ||
792 | return -EFAULT; | ||
793 | reverb_parm[mode] = rec; | ||
794 | reverb_defined[mode] = 1; | ||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | /*exported*/ void | ||
799 | snd_emu8000_update_reverb_mode(emu8000_t *emu) | ||
800 | { | ||
801 | int effect = emu->reverb_mode; | ||
802 | int i; | ||
803 | |||
804 | if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS || | ||
805 | (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect])) | ||
806 | return; | ||
807 | for (i = 0; i < 28; i++) { | ||
808 | int port; | ||
809 | if (reverb_cmds[i].port == DATA1) | ||
810 | port = EMU8000_DATA1(emu); | ||
811 | else | ||
812 | port = EMU8000_DATA2(emu); | ||
813 | snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]); | ||
814 | } | ||
815 | } | ||
816 | |||
817 | |||
818 | /*---------------------------------------------------------------- | ||
819 | * mixer interface | ||
820 | *----------------------------------------------------------------*/ | ||
821 | |||
822 | /* | ||
823 | * bass/treble | ||
824 | */ | ||
825 | static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
826 | { | ||
827 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
828 | uinfo->count = 1; | ||
829 | uinfo->value.integer.min = 0; | ||
830 | uinfo->value.integer.max = 11; | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
835 | { | ||
836 | emu8000_t *emu = snd_kcontrol_chip(kcontrol); | ||
837 | |||
838 | ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level; | ||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
843 | { | ||
844 | emu8000_t *emu = snd_kcontrol_chip(kcontrol); | ||
845 | unsigned long flags; | ||
846 | int change; | ||
847 | unsigned short val1; | ||
848 | |||
849 | val1 = ucontrol->value.integer.value[0] % 12; | ||
850 | spin_lock_irqsave(&emu->control_lock, flags); | ||
851 | if (kcontrol->private_value) { | ||
852 | change = val1 != emu->treble_level; | ||
853 | emu->treble_level = val1; | ||
854 | } else { | ||
855 | change = val1 != emu->bass_level; | ||
856 | emu->bass_level = val1; | ||
857 | } | ||
858 | spin_unlock_irqrestore(&emu->control_lock, flags); | ||
859 | snd_emu8000_update_equalizer(emu); | ||
860 | return change; | ||
861 | } | ||
862 | |||
863 | static snd_kcontrol_new_t mixer_bass_control = | ||
864 | { | ||
865 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
866 | .name = "Synth Tone Control - Bass", | ||
867 | .info = mixer_bass_treble_info, | ||
868 | .get = mixer_bass_treble_get, | ||
869 | .put = mixer_bass_treble_put, | ||
870 | .private_value = 0, | ||
871 | }; | ||
872 | |||
873 | static snd_kcontrol_new_t mixer_treble_control = | ||
874 | { | ||
875 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
876 | .name = "Synth Tone Control - Treble", | ||
877 | .info = mixer_bass_treble_info, | ||
878 | .get = mixer_bass_treble_get, | ||
879 | .put = mixer_bass_treble_put, | ||
880 | .private_value = 1, | ||
881 | }; | ||
882 | |||
883 | /* | ||
884 | * chorus/reverb mode | ||
885 | */ | ||
886 | static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
887 | { | ||
888 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
889 | uinfo->count = 1; | ||
890 | uinfo->value.integer.min = 0; | ||
891 | uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1); | ||
892 | return 0; | ||
893 | } | ||
894 | |||
895 | static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
896 | { | ||
897 | emu8000_t *emu = snd_kcontrol_chip(kcontrol); | ||
898 | |||
899 | ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode; | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
904 | { | ||
905 | emu8000_t *emu = snd_kcontrol_chip(kcontrol); | ||
906 | unsigned long flags; | ||
907 | int change; | ||
908 | unsigned short val1; | ||
909 | |||
910 | spin_lock_irqsave(&emu->control_lock, flags); | ||
911 | if (kcontrol->private_value) { | ||
912 | val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS; | ||
913 | change = val1 != emu->chorus_mode; | ||
914 | emu->chorus_mode = val1; | ||
915 | } else { | ||
916 | val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS; | ||
917 | change = val1 != emu->reverb_mode; | ||
918 | emu->reverb_mode = val1; | ||
919 | } | ||
920 | spin_unlock_irqrestore(&emu->control_lock, flags); | ||
921 | if (change) { | ||
922 | if (kcontrol->private_value) | ||
923 | snd_emu8000_update_chorus_mode(emu); | ||
924 | else | ||
925 | snd_emu8000_update_reverb_mode(emu); | ||
926 | } | ||
927 | return change; | ||
928 | } | ||
929 | |||
930 | static snd_kcontrol_new_t mixer_chorus_mode_control = | ||
931 | { | ||
932 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
933 | .name = "Chorus Mode", | ||
934 | .info = mixer_chorus_reverb_info, | ||
935 | .get = mixer_chorus_reverb_get, | ||
936 | .put = mixer_chorus_reverb_put, | ||
937 | .private_value = 1, | ||
938 | }; | ||
939 | |||
940 | static snd_kcontrol_new_t mixer_reverb_mode_control = | ||
941 | { | ||
942 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
943 | .name = "Reverb Mode", | ||
944 | .info = mixer_chorus_reverb_info, | ||
945 | .get = mixer_chorus_reverb_get, | ||
946 | .put = mixer_chorus_reverb_put, | ||
947 | .private_value = 0, | ||
948 | }; | ||
949 | |||
950 | /* | ||
951 | * FM OPL3 chorus/reverb depth | ||
952 | */ | ||
953 | static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
954 | { | ||
955 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
956 | uinfo->count = 1; | ||
957 | uinfo->value.integer.min = 0; | ||
958 | uinfo->value.integer.max = 255; | ||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
963 | { | ||
964 | emu8000_t *emu = snd_kcontrol_chip(kcontrol); | ||
965 | |||
966 | ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth; | ||
967 | return 0; | ||
968 | } | ||
969 | |||
970 | static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
971 | { | ||
972 | emu8000_t *emu = snd_kcontrol_chip(kcontrol); | ||
973 | unsigned long flags; | ||
974 | int change; | ||
975 | unsigned short val1; | ||
976 | |||
977 | val1 = ucontrol->value.integer.value[0] % 256; | ||
978 | spin_lock_irqsave(&emu->control_lock, flags); | ||
979 | if (kcontrol->private_value) { | ||
980 | change = val1 != emu->fm_chorus_depth; | ||
981 | emu->fm_chorus_depth = val1; | ||
982 | } else { | ||
983 | change = val1 != emu->fm_reverb_depth; | ||
984 | emu->fm_reverb_depth = val1; | ||
985 | } | ||
986 | spin_unlock_irqrestore(&emu->control_lock, flags); | ||
987 | if (change) | ||
988 | snd_emu8000_init_fm(emu); | ||
989 | return change; | ||
990 | } | ||
991 | |||
992 | static snd_kcontrol_new_t mixer_fm_chorus_depth_control = | ||
993 | { | ||
994 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
995 | .name = "FM Chorus Depth", | ||
996 | .info = mixer_fm_depth_info, | ||
997 | .get = mixer_fm_depth_get, | ||
998 | .put = mixer_fm_depth_put, | ||
999 | .private_value = 1, | ||
1000 | }; | ||
1001 | |||
1002 | static snd_kcontrol_new_t mixer_fm_reverb_depth_control = | ||
1003 | { | ||
1004 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1005 | .name = "FM Reverb Depth", | ||
1006 | .info = mixer_fm_depth_info, | ||
1007 | .get = mixer_fm_depth_get, | ||
1008 | .put = mixer_fm_depth_put, | ||
1009 | .private_value = 0, | ||
1010 | }; | ||
1011 | |||
1012 | |||
1013 | static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = { | ||
1014 | &mixer_bass_control, | ||
1015 | &mixer_treble_control, | ||
1016 | &mixer_chorus_mode_control, | ||
1017 | &mixer_reverb_mode_control, | ||
1018 | &mixer_fm_chorus_depth_control, | ||
1019 | &mixer_fm_reverb_depth_control, | ||
1020 | }; | ||
1021 | |||
1022 | /* | ||
1023 | * create and attach mixer elements for WaveTable treble/bass controls | ||
1024 | */ | ||
1025 | static int __init | ||
1026 | snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu) | ||
1027 | { | ||
1028 | int i, err = 0; | ||
1029 | |||
1030 | snd_assert(emu != NULL && card != NULL, return -EINVAL); | ||
1031 | |||
1032 | spin_lock_init(&emu->control_lock); | ||
1033 | |||
1034 | memset(emu->controls, 0, sizeof(emu->controls)); | ||
1035 | for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { | ||
1036 | if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0) | ||
1037 | goto __error; | ||
1038 | } | ||
1039 | return 0; | ||
1040 | |||
1041 | __error: | ||
1042 | for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { | ||
1043 | down_write(&card->controls_rwsem); | ||
1044 | if (emu->controls[i]) | ||
1045 | snd_ctl_remove(card, emu->controls[i]); | ||
1046 | up_write(&card->controls_rwsem); | ||
1047 | } | ||
1048 | return err; | ||
1049 | } | ||
1050 | |||
1051 | |||
1052 | /* | ||
1053 | * free resources | ||
1054 | */ | ||
1055 | static int snd_emu8000_free(emu8000_t *hw) | ||
1056 | { | ||
1057 | if (hw->res_port1) { | ||
1058 | release_resource(hw->res_port1); | ||
1059 | kfree_nocheck(hw->res_port1); | ||
1060 | } | ||
1061 | if (hw->res_port2) { | ||
1062 | release_resource(hw->res_port2); | ||
1063 | kfree_nocheck(hw->res_port2); | ||
1064 | } | ||
1065 | if (hw->res_port3) { | ||
1066 | release_resource(hw->res_port3); | ||
1067 | kfree_nocheck(hw->res_port3); | ||
1068 | } | ||
1069 | kfree(hw); | ||
1070 | return 0; | ||
1071 | } | ||
1072 | |||
1073 | /* | ||
1074 | */ | ||
1075 | static int snd_emu8000_dev_free(snd_device_t *device) | ||
1076 | { | ||
1077 | emu8000_t *hw = device->device_data; | ||
1078 | return snd_emu8000_free(hw); | ||
1079 | } | ||
1080 | |||
1081 | /* | ||
1082 | * initialize and register emu8000 synth device. | ||
1083 | */ | ||
1084 | int __init | ||
1085 | snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret) | ||
1086 | { | ||
1087 | snd_seq_device_t *awe; | ||
1088 | emu8000_t *hw; | ||
1089 | int err; | ||
1090 | static snd_device_ops_t ops = { | ||
1091 | .dev_free = snd_emu8000_dev_free, | ||
1092 | }; | ||
1093 | |||
1094 | if (awe_ret) | ||
1095 | *awe_ret = NULL; | ||
1096 | |||
1097 | if (seq_ports <= 0) | ||
1098 | return 0; | ||
1099 | |||
1100 | hw = kcalloc(1, sizeof(*hw), GFP_KERNEL); | ||
1101 | if (hw == NULL) | ||
1102 | return -ENOMEM; | ||
1103 | spin_lock_init(&hw->reg_lock); | ||
1104 | hw->index = index; | ||
1105 | hw->port1 = port; | ||
1106 | hw->port2 = port + 0x400; | ||
1107 | hw->port3 = port + 0x800; | ||
1108 | if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) || | ||
1109 | !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) || | ||
1110 | !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) { | ||
1111 | snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3); | ||
1112 | snd_emu8000_free(hw); | ||
1113 | return -EBUSY; | ||
1114 | } | ||
1115 | hw->mem_size = 0; | ||
1116 | hw->card = card; | ||
1117 | hw->seq_ports = seq_ports; | ||
1118 | hw->bass_level = 5; | ||
1119 | hw->treble_level = 9; | ||
1120 | hw->chorus_mode = 2; | ||
1121 | hw->reverb_mode = 4; | ||
1122 | hw->fm_chorus_depth = 0; | ||
1123 | hw->fm_reverb_depth = 0; | ||
1124 | |||
1125 | if (snd_emu8000_detect(hw) < 0) { | ||
1126 | snd_emu8000_free(hw); | ||
1127 | return -ENODEV; | ||
1128 | } | ||
1129 | |||
1130 | snd_emu8000_init_hw(hw); | ||
1131 | if ((err = snd_emu8000_create_mixer(card, hw)) < 0) { | ||
1132 | snd_emu8000_free(hw); | ||
1133 | return err; | ||
1134 | } | ||
1135 | |||
1136 | if ((err = snd_device_new(card, SNDRV_DEV_CODEC, hw, &ops)) < 0) { | ||
1137 | snd_emu8000_free(hw); | ||
1138 | return err; | ||
1139 | } | ||
1140 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
1141 | if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000, | ||
1142 | sizeof(emu8000_t*), &awe) >= 0) { | ||
1143 | strcpy(awe->name, "EMU-8000"); | ||
1144 | *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw; | ||
1145 | } | ||
1146 | #else | ||
1147 | awe = NULL; | ||
1148 | #endif | ||
1149 | if (awe_ret) | ||
1150 | *awe_ret = awe; | ||
1151 | |||
1152 | return 0; | ||
1153 | } | ||
1154 | |||
1155 | |||
1156 | /* | ||
1157 | * exported stuff | ||
1158 | */ | ||
1159 | |||
1160 | EXPORT_SYMBOL(snd_emu8000_poke); | ||
1161 | EXPORT_SYMBOL(snd_emu8000_peek); | ||
1162 | EXPORT_SYMBOL(snd_emu8000_poke_dw); | ||
1163 | EXPORT_SYMBOL(snd_emu8000_peek_dw); | ||
1164 | EXPORT_SYMBOL(snd_emu8000_dma_chan); | ||
1165 | EXPORT_SYMBOL(snd_emu8000_init_fm); | ||
1166 | EXPORT_SYMBOL(snd_emu8000_load_chorus_fx); | ||
1167 | EXPORT_SYMBOL(snd_emu8000_load_reverb_fx); | ||
1168 | EXPORT_SYMBOL(snd_emu8000_update_chorus_mode); | ||
1169 | EXPORT_SYMBOL(snd_emu8000_update_reverb_mode); | ||
1170 | EXPORT_SYMBOL(snd_emu8000_update_equalizer); | ||
diff --git a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c new file mode 100644 index 000000000000..1cc4101a17a4 --- /dev/null +++ b/sound/isa/sb/emu8000_callback.c | |||
@@ -0,0 +1,543 @@ | |||
1 | /* | ||
2 | * synth callback routines for the emu8000 (AWE32/64) | ||
3 | * | ||
4 | * Copyright (C) 1999 Steve Ratcliffe | ||
5 | * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include "emu8000_local.h" | ||
23 | #include <sound/asoundef.h> | ||
24 | |||
25 | /* | ||
26 | * prototypes | ||
27 | */ | ||
28 | static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); | ||
29 | static int start_voice(snd_emux_voice_t *vp); | ||
30 | static void trigger_voice(snd_emux_voice_t *vp); | ||
31 | static void release_voice(snd_emux_voice_t *vp); | ||
32 | static void update_voice(snd_emux_voice_t *vp, int update); | ||
33 | static void reset_voice(snd_emux_t *emu, int ch); | ||
34 | static void terminate_voice(snd_emux_voice_t *vp); | ||
35 | static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); | ||
36 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
37 | static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2); | ||
38 | #endif | ||
39 | static int load_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len); | ||
40 | |||
41 | static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp); | ||
42 | static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp); | ||
43 | static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp); | ||
44 | static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp); | ||
45 | static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp); | ||
46 | static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp); | ||
47 | static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp); | ||
48 | static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch); | ||
49 | |||
50 | /* | ||
51 | * Ensure a value is between two points | ||
52 | * macro evaluates its args more than once, so changed to upper-case. | ||
53 | */ | ||
54 | #define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) | ||
55 | #define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) | ||
56 | |||
57 | |||
58 | /* | ||
59 | * set up operators | ||
60 | */ | ||
61 | static snd_emux_operators_t emu8000_ops = { | ||
62 | .owner = THIS_MODULE, | ||
63 | .get_voice = get_voice, | ||
64 | .prepare = start_voice, | ||
65 | .trigger = trigger_voice, | ||
66 | .release = release_voice, | ||
67 | .update = update_voice, | ||
68 | .terminate = terminate_voice, | ||
69 | .reset = reset_voice, | ||
70 | .sample_new = snd_emu8000_sample_new, | ||
71 | .sample_free = snd_emu8000_sample_free, | ||
72 | .sample_reset = snd_emu8000_sample_reset, | ||
73 | .load_fx = load_fx, | ||
74 | .sysex = sysex, | ||
75 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
76 | .oss_ioctl = oss_ioctl, | ||
77 | #endif | ||
78 | }; | ||
79 | |||
80 | void | ||
81 | snd_emu8000_ops_setup(emu8000_t *hw) | ||
82 | { | ||
83 | hw->emu->ops = emu8000_ops; | ||
84 | } | ||
85 | |||
86 | |||
87 | |||
88 | /* | ||
89 | * Terminate a voice | ||
90 | */ | ||
91 | static void | ||
92 | release_voice(snd_emux_voice_t *vp) | ||
93 | { | ||
94 | int dcysusv; | ||
95 | emu8000_t *hw; | ||
96 | |||
97 | hw = vp->hw; | ||
98 | dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; | ||
99 | EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); | ||
100 | dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; | ||
101 | EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv); | ||
102 | } | ||
103 | |||
104 | |||
105 | /* | ||
106 | */ | ||
107 | static void | ||
108 | terminate_voice(snd_emux_voice_t *vp) | ||
109 | { | ||
110 | emu8000_t *hw; | ||
111 | |||
112 | hw = vp->hw; | ||
113 | EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); | ||
114 | } | ||
115 | |||
116 | |||
117 | /* | ||
118 | */ | ||
119 | static void | ||
120 | update_voice(snd_emux_voice_t *vp, int update) | ||
121 | { | ||
122 | emu8000_t *hw; | ||
123 | |||
124 | hw = vp->hw; | ||
125 | if (update & SNDRV_EMUX_UPDATE_VOLUME) | ||
126 | set_volume(hw, vp); | ||
127 | if (update & SNDRV_EMUX_UPDATE_PITCH) | ||
128 | set_pitch(hw, vp); | ||
129 | if ((update & SNDRV_EMUX_UPDATE_PAN) && | ||
130 | vp->port->ctrls[EMUX_MD_REALTIME_PAN]) | ||
131 | set_pan(hw, vp); | ||
132 | if (update & SNDRV_EMUX_UPDATE_FMMOD) | ||
133 | set_fmmod(hw, vp); | ||
134 | if (update & SNDRV_EMUX_UPDATE_TREMFREQ) | ||
135 | set_tremfreq(hw, vp); | ||
136 | if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) | ||
137 | set_fm2frq2(hw, vp); | ||
138 | if (update & SNDRV_EMUX_UPDATE_Q) | ||
139 | set_filterQ(hw, vp); | ||
140 | } | ||
141 | |||
142 | |||
143 | /* | ||
144 | * Find a channel (voice) within the EMU that is not in use or at least | ||
145 | * less in use than other channels. Always returns a valid pointer | ||
146 | * no matter what. If there is a real shortage of voices then one | ||
147 | * will be cut. Such is life. | ||
148 | * | ||
149 | * The channel index (vp->ch) must be initialized in this routine. | ||
150 | * In Emu8k, it is identical with the array index. | ||
151 | */ | ||
152 | static snd_emux_voice_t * | ||
153 | get_voice(snd_emux_t *emu, snd_emux_port_t *port) | ||
154 | { | ||
155 | int i; | ||
156 | snd_emux_voice_t *vp; | ||
157 | emu8000_t *hw; | ||
158 | |||
159 | /* what we are looking for, in order of preference */ | ||
160 | enum { | ||
161 | OFF=0, RELEASED, PLAYING, END | ||
162 | }; | ||
163 | |||
164 | /* Keeps track of what we are finding */ | ||
165 | struct best { | ||
166 | unsigned int time; | ||
167 | int voice; | ||
168 | } best[END]; | ||
169 | struct best *bp; | ||
170 | |||
171 | hw = emu->hw; | ||
172 | |||
173 | for (i = 0; i < END; i++) { | ||
174 | best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; | ||
175 | best[i].voice = -1; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Go through them all and get a best one to use. | ||
180 | */ | ||
181 | for (i = 0; i < emu->max_voices; i++) { | ||
182 | int state, val; | ||
183 | |||
184 | vp = &emu->voices[i]; | ||
185 | state = vp->state; | ||
186 | |||
187 | if (state == SNDRV_EMUX_ST_OFF) | ||
188 | bp = best + OFF; | ||
189 | else if (state == SNDRV_EMUX_ST_RELEASED || | ||
190 | state == SNDRV_EMUX_ST_PENDING) { | ||
191 | bp = best + RELEASED; | ||
192 | val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff; | ||
193 | if (! val) | ||
194 | bp = best + OFF; | ||
195 | } | ||
196 | else if (state & SNDRV_EMUX_ST_ON) | ||
197 | bp = best + PLAYING; | ||
198 | else | ||
199 | continue; | ||
200 | |||
201 | /* check if sample is finished playing (non-looping only) */ | ||
202 | if (state != SNDRV_EMUX_ST_OFF && | ||
203 | (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { | ||
204 | val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; | ||
205 | if (val >= vp->reg.loopstart) | ||
206 | bp = best + OFF; | ||
207 | } | ||
208 | |||
209 | if (vp->time < bp->time) { | ||
210 | bp->time = vp->time; | ||
211 | bp->voice = i; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | for (i = 0; i < END; i++) { | ||
216 | if (best[i].voice >= 0) { | ||
217 | vp = &emu->voices[best[i].voice]; | ||
218 | vp->ch = best[i].voice; | ||
219 | return vp; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | /* not found */ | ||
224 | return NULL; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | */ | ||
229 | static int | ||
230 | start_voice(snd_emux_voice_t *vp) | ||
231 | { | ||
232 | unsigned int temp; | ||
233 | int ch; | ||
234 | int addr; | ||
235 | snd_midi_channel_t *chan; | ||
236 | emu8000_t *hw; | ||
237 | |||
238 | hw = vp->hw; | ||
239 | ch = vp->ch; | ||
240 | chan = vp->chan; | ||
241 | |||
242 | /* channel to be silent and idle */ | ||
243 | EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); | ||
244 | EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); | ||
245 | EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); | ||
246 | EMU8000_PTRX_WRITE(hw, ch, 0); | ||
247 | EMU8000_CPF_WRITE(hw, ch, 0); | ||
248 | |||
249 | /* set pitch offset */ | ||
250 | set_pitch(hw, vp); | ||
251 | |||
252 | /* set envelope parameters */ | ||
253 | EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay); | ||
254 | EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld); | ||
255 | EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus); | ||
256 | EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay); | ||
257 | EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld); | ||
258 | /* decay/sustain parameter for volume envelope is used | ||
259 | for triggerg the voice */ | ||
260 | |||
261 | /* cutoff and volume */ | ||
262 | set_volume(hw, vp); | ||
263 | |||
264 | /* modulation envelope heights */ | ||
265 | EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe); | ||
266 | |||
267 | /* lfo1/2 delay */ | ||
268 | EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay); | ||
269 | EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay); | ||
270 | |||
271 | /* lfo1 pitch & cutoff shift */ | ||
272 | set_fmmod(hw, vp); | ||
273 | /* lfo1 volume & freq */ | ||
274 | set_tremfreq(hw, vp); | ||
275 | /* lfo2 pitch & freq */ | ||
276 | set_fm2frq2(hw, vp); | ||
277 | /* pan & loop start */ | ||
278 | set_pan(hw, vp); | ||
279 | |||
280 | /* chorus & loop end (chorus 8bit, MSB) */ | ||
281 | addr = vp->reg.loopend - 1; | ||
282 | temp = vp->reg.parm.chorus; | ||
283 | temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; | ||
284 | LIMITMAX(temp, 255); | ||
285 | temp = (temp <<24) | (unsigned int)addr; | ||
286 | EMU8000_CSL_WRITE(hw, ch, temp); | ||
287 | |||
288 | /* Q & current address (Q 4bit value, MSB) */ | ||
289 | addr = vp->reg.start - 1; | ||
290 | temp = vp->reg.parm.filterQ; | ||
291 | temp = (temp<<28) | (unsigned int)addr; | ||
292 | EMU8000_CCCA_WRITE(hw, ch, temp); | ||
293 | |||
294 | /* clear unknown registers */ | ||
295 | EMU8000_00A0_WRITE(hw, ch, 0); | ||
296 | EMU8000_0080_WRITE(hw, ch, 0); | ||
297 | |||
298 | /* reset volume */ | ||
299 | temp = vp->vtarget << 16; | ||
300 | EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget); | ||
301 | EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00); | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Start envelope | ||
308 | */ | ||
309 | static void | ||
310 | trigger_voice(snd_emux_voice_t *vp) | ||
311 | { | ||
312 | int ch = vp->ch; | ||
313 | unsigned int temp; | ||
314 | emu8000_t *hw; | ||
315 | |||
316 | hw = vp->hw; | ||
317 | |||
318 | /* set reverb and pitch target */ | ||
319 | temp = vp->reg.parm.reverb; | ||
320 | temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; | ||
321 | LIMITMAX(temp, 255); | ||
322 | temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux; | ||
323 | EMU8000_PTRX_WRITE(hw, ch, temp); | ||
324 | EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16); | ||
325 | EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * reset voice parameters | ||
330 | */ | ||
331 | static void | ||
332 | reset_voice(snd_emux_t *emu, int ch) | ||
333 | { | ||
334 | emu8000_t *hw; | ||
335 | |||
336 | hw = emu->hw; | ||
337 | EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); | ||
338 | snd_emu8000_tweak_voice(hw, ch); | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * Set the pitch of a possibly playing note. | ||
343 | */ | ||
344 | static void | ||
345 | set_pitch(emu8000_t *hw, snd_emux_voice_t *vp) | ||
346 | { | ||
347 | EMU8000_IP_WRITE(hw, vp->ch, vp->apitch); | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * Set the volume of a possibly already playing note | ||
352 | */ | ||
353 | static void | ||
354 | set_volume(emu8000_t *hw, snd_emux_voice_t *vp) | ||
355 | { | ||
356 | int ifatn; | ||
357 | |||
358 | ifatn = (unsigned char)vp->acutoff; | ||
359 | ifatn = (ifatn << 8); | ||
360 | ifatn |= (unsigned char)vp->avol; | ||
361 | EMU8000_IFATN_WRITE(hw, vp->ch, ifatn); | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * Set pan and loop start address. | ||
366 | */ | ||
367 | static void | ||
368 | set_pan(emu8000_t *hw, snd_emux_voice_t *vp) | ||
369 | { | ||
370 | unsigned int temp; | ||
371 | |||
372 | temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1); | ||
373 | EMU8000_PSST_WRITE(hw, vp->ch, temp); | ||
374 | } | ||
375 | |||
376 | #define MOD_SENSE 18 | ||
377 | |||
378 | static void | ||
379 | set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp) | ||
380 | { | ||
381 | unsigned short fmmod; | ||
382 | short pitch; | ||
383 | unsigned char cutoff; | ||
384 | int modulation; | ||
385 | |||
386 | pitch = (char)(vp->reg.parm.fmmod>>8); | ||
387 | cutoff = (vp->reg.parm.fmmod & 0xff); | ||
388 | modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; | ||
389 | pitch += (MOD_SENSE * modulation) / 1200; | ||
390 | LIMITVALUE(pitch, -128, 127); | ||
391 | fmmod = ((unsigned char)pitch<<8) | cutoff; | ||
392 | EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod); | ||
393 | } | ||
394 | |||
395 | /* set tremolo (lfo1) volume & frequency */ | ||
396 | static void | ||
397 | set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp) | ||
398 | { | ||
399 | EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq); | ||
400 | } | ||
401 | |||
402 | /* set lfo2 pitch & frequency */ | ||
403 | static void | ||
404 | set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp) | ||
405 | { | ||
406 | unsigned short fm2frq2; | ||
407 | short pitch; | ||
408 | unsigned char freq; | ||
409 | int modulation; | ||
410 | |||
411 | pitch = (char)(vp->reg.parm.fm2frq2>>8); | ||
412 | freq = vp->reg.parm.fm2frq2 & 0xff; | ||
413 | modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; | ||
414 | pitch += (MOD_SENSE * modulation) / 1200; | ||
415 | LIMITVALUE(pitch, -128, 127); | ||
416 | fm2frq2 = ((unsigned char)pitch<<8) | freq; | ||
417 | EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2); | ||
418 | } | ||
419 | |||
420 | /* set filterQ */ | ||
421 | static void | ||
422 | set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp) | ||
423 | { | ||
424 | unsigned int addr; | ||
425 | addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; | ||
426 | addr |= (vp->reg.parm.filterQ << 28); | ||
427 | EMU8000_CCCA_WRITE(hw, vp->ch, addr); | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * set the envelope & LFO parameters to the default values | ||
432 | */ | ||
433 | static void | ||
434 | snd_emu8000_tweak_voice(emu8000_t *emu, int i) | ||
435 | { | ||
436 | /* set all mod/vol envelope shape to minimum */ | ||
437 | EMU8000_ENVVOL_WRITE(emu, i, 0x8000); | ||
438 | EMU8000_ENVVAL_WRITE(emu, i, 0x8000); | ||
439 | EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F); | ||
440 | EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F); | ||
441 | EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F); | ||
442 | EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */ | ||
443 | EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */ | ||
444 | EMU8000_LFO2VAL_WRITE(emu, i, 0x8000); | ||
445 | EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */ | ||
446 | EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */ | ||
447 | EMU8000_FMMOD_WRITE(emu, i, 0); | ||
448 | EMU8000_TREMFRQ_WRITE(emu, i, 0); | ||
449 | EMU8000_FM2FRQ2_WRITE(emu, i, 0); | ||
450 | } | ||
451 | |||
452 | /* | ||
453 | * sysex callback | ||
454 | */ | ||
455 | static void | ||
456 | sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset) | ||
457 | { | ||
458 | emu8000_t *hw; | ||
459 | |||
460 | hw = emu->hw; | ||
461 | |||
462 | switch (parsed) { | ||
463 | case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: | ||
464 | hw->chorus_mode = chset->gs_chorus_mode; | ||
465 | snd_emu8000_update_chorus_mode(hw); | ||
466 | break; | ||
467 | |||
468 | case SNDRV_MIDI_SYSEX_GS_REVERB_MODE: | ||
469 | hw->reverb_mode = chset->gs_reverb_mode; | ||
470 | snd_emu8000_update_reverb_mode(hw); | ||
471 | break; | ||
472 | } | ||
473 | } | ||
474 | |||
475 | |||
476 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
477 | /* | ||
478 | * OSS ioctl callback | ||
479 | */ | ||
480 | static int | ||
481 | oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2) | ||
482 | { | ||
483 | emu8000_t *hw; | ||
484 | |||
485 | hw = emu->hw; | ||
486 | |||
487 | switch (cmd) { | ||
488 | case _EMUX_OSS_REVERB_MODE: | ||
489 | hw->reverb_mode = p1; | ||
490 | snd_emu8000_update_reverb_mode(hw); | ||
491 | break; | ||
492 | |||
493 | case _EMUX_OSS_CHORUS_MODE: | ||
494 | hw->chorus_mode = p1; | ||
495 | snd_emu8000_update_chorus_mode(hw); | ||
496 | break; | ||
497 | |||
498 | case _EMUX_OSS_INITIALIZE_CHIP: | ||
499 | /* snd_emu8000_init(hw); */ /*ignored*/ | ||
500 | break; | ||
501 | |||
502 | case _EMUX_OSS_EQUALIZER: | ||
503 | hw->bass_level = p1; | ||
504 | hw->treble_level = p2; | ||
505 | snd_emu8000_update_equalizer(hw); | ||
506 | break; | ||
507 | } | ||
508 | return 0; | ||
509 | } | ||
510 | #endif | ||
511 | |||
512 | |||
513 | /* | ||
514 | * additional patch keys | ||
515 | */ | ||
516 | |||
517 | #define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */ | ||
518 | #define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */ | ||
519 | |||
520 | |||
521 | /* | ||
522 | * callback routine | ||
523 | */ | ||
524 | |||
525 | static int | ||
526 | load_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len) | ||
527 | { | ||
528 | emu8000_t *hw; | ||
529 | hw = emu->hw; | ||
530 | |||
531 | /* skip header */ | ||
532 | buf += 16; | ||
533 | len -= 16; | ||
534 | |||
535 | switch (type) { | ||
536 | case SNDRV_EMU8000_LOAD_CHORUS_FX: | ||
537 | return snd_emu8000_load_chorus_fx(hw, mode, buf, len); | ||
538 | case SNDRV_EMU8000_LOAD_REVERB_FX: | ||
539 | return snd_emu8000_load_reverb_fx(hw, mode, buf, len); | ||
540 | } | ||
541 | return -EINVAL; | ||
542 | } | ||
543 | |||
diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h new file mode 100644 index 000000000000..ea4996a895fc --- /dev/null +++ b/sound/isa/sb/emu8000_local.h | |||
@@ -0,0 +1,43 @@ | |||
1 | #ifndef __EMU8000_LOCAL_H | ||
2 | #define __EMU8000_LOCAL_H | ||
3 | /* | ||
4 | * Local defininitons for the emu8000 (AWE32/64) | ||
5 | * | ||
6 | * Copyright (C) 1999 Steve Ratcliffe | ||
7 | * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/wait.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/emu8000.h> | ||
30 | #include <sound/emu8000_reg.h> | ||
31 | |||
32 | /* emu8000_patch.c */ | ||
33 | int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void __user *data, long count); | ||
34 | int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); | ||
35 | void snd_emu8000_sample_reset(snd_emux_t *rec); | ||
36 | |||
37 | /* emu8000_callback.c */ | ||
38 | void snd_emu8000_ops_setup(emu8000_t *emu); | ||
39 | |||
40 | /* emu8000_pcm.c */ | ||
41 | int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index); | ||
42 | |||
43 | #endif /* __EMU8000_LOCAL_H */ | ||
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c new file mode 100644 index 000000000000..4afc4a1bc140 --- /dev/null +++ b/sound/isa/sb/emu8000_patch.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /* | ||
2 | * Patch routines for the emu8000 (AWE32/64) | ||
3 | * | ||
4 | * Copyright (C) 1999 Steve Ratcliffe | ||
5 | * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include "emu8000_local.h" | ||
23 | #include <asm/uaccess.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | |||
26 | static int emu8000_reset_addr = 0; | ||
27 | module_param(emu8000_reset_addr, int, 0444); | ||
28 | MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)"); | ||
29 | |||
30 | |||
31 | /* | ||
32 | * Open up channels. | ||
33 | */ | ||
34 | static int | ||
35 | snd_emu8000_open_dma(emu8000_t *emu, int write) | ||
36 | { | ||
37 | int i; | ||
38 | |||
39 | /* reserve all 30 voices for loading */ | ||
40 | for (i = 0; i < EMU8000_DRAM_VOICES; i++) { | ||
41 | snd_emux_lock_voice(emu->emu, i); | ||
42 | snd_emu8000_dma_chan(emu, i, write); | ||
43 | } | ||
44 | |||
45 | /* assign voice 31 and 32 to ROM */ | ||
46 | EMU8000_VTFT_WRITE(emu, 30, 0); | ||
47 | EMU8000_PSST_WRITE(emu, 30, 0x1d8); | ||
48 | EMU8000_CSL_WRITE(emu, 30, 0x1e0); | ||
49 | EMU8000_CCCA_WRITE(emu, 30, 0x1d8); | ||
50 | EMU8000_VTFT_WRITE(emu, 31, 0); | ||
51 | EMU8000_PSST_WRITE(emu, 31, 0x1d8); | ||
52 | EMU8000_CSL_WRITE(emu, 31, 0x1e0); | ||
53 | EMU8000_CCCA_WRITE(emu, 31, 0x1d8); | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * Close all dram channels. | ||
60 | */ | ||
61 | static void | ||
62 | snd_emu8000_close_dma(emu8000_t *emu) | ||
63 | { | ||
64 | int i; | ||
65 | |||
66 | for (i = 0; i < EMU8000_DRAM_VOICES; i++) { | ||
67 | snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); | ||
68 | snd_emux_unlock_voice(emu->emu, i); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | */ | ||
74 | |||
75 | #define BLANK_LOOP_START 4 | ||
76 | #define BLANK_LOOP_END 8 | ||
77 | #define BLANK_LOOP_SIZE 12 | ||
78 | #define BLANK_HEAD_SIZE 48 | ||
79 | |||
80 | /* | ||
81 | * Read a word from userland, taking care of conversions from | ||
82 | * 8bit samples etc. | ||
83 | */ | ||
84 | static unsigned short | ||
85 | read_word(const void __user *buf, int offset, int mode) | ||
86 | { | ||
87 | unsigned short c; | ||
88 | if (mode & SNDRV_SFNT_SAMPLE_8BITS) { | ||
89 | unsigned char cc; | ||
90 | get_user(cc, (unsigned char __user *)buf + offset); | ||
91 | c = cc << 8; /* convert 8bit -> 16bit */ | ||
92 | } else { | ||
93 | #ifdef SNDRV_LITTLE_ENDIAN | ||
94 | get_user(c, (unsigned short __user *)buf + offset); | ||
95 | #else | ||
96 | unsigned short cc; | ||
97 | get_user(cc, (unsigned short __user *)buf + offset); | ||
98 | c = swab16(cc); | ||
99 | #endif | ||
100 | } | ||
101 | if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED) | ||
102 | c ^= 0x8000; /* unsigned -> signed */ | ||
103 | return c; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | */ | ||
108 | static void | ||
109 | snd_emu8000_write_wait(emu8000_t *emu) | ||
110 | { | ||
111 | while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { | ||
112 | set_current_state(TASK_INTERRUPTIBLE); | ||
113 | schedule_timeout(1); | ||
114 | if (signal_pending(current)) | ||
115 | break; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * write sample word data | ||
121 | * | ||
122 | * You should not have to keep resetting the address each time | ||
123 | * as the chip is supposed to step on the next address automatically. | ||
124 | * It mostly does, but during writes of some samples at random it | ||
125 | * completely loses words (every one in 16 roughly but with no | ||
126 | * obvious pattern). | ||
127 | * | ||
128 | * This is therefore much slower than need be, but is at least | ||
129 | * working. | ||
130 | */ | ||
131 | inline static void | ||
132 | write_word(emu8000_t *emu, int *offset, unsigned short data) | ||
133 | { | ||
134 | if (emu8000_reset_addr) { | ||
135 | if (emu8000_reset_addr > 1) | ||
136 | snd_emu8000_write_wait(emu); | ||
137 | EMU8000_SMALW_WRITE(emu, *offset); | ||
138 | } | ||
139 | EMU8000_SMLD_WRITE(emu, data); | ||
140 | *offset += 1; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Write the sample to EMU800 memory. This routine is invoked out of | ||
145 | * the generic soundfont routines as a callback. | ||
146 | */ | ||
147 | int | ||
148 | snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, | ||
149 | snd_util_memhdr_t *hdr, const void __user *data, long count) | ||
150 | { | ||
151 | int i; | ||
152 | int rc; | ||
153 | int offset; | ||
154 | int truesize; | ||
155 | int dram_offset, dram_start; | ||
156 | emu8000_t *emu; | ||
157 | |||
158 | emu = rec->hw; | ||
159 | snd_assert(sp != NULL, return -EINVAL); | ||
160 | |||
161 | if (sp->v.size == 0) | ||
162 | return 0; | ||
163 | |||
164 | /* be sure loop points start < end */ | ||
165 | if (sp->v.loopstart > sp->v.loopend) { | ||
166 | int tmp = sp->v.loopstart; | ||
167 | sp->v.loopstart = sp->v.loopend; | ||
168 | sp->v.loopend = tmp; | ||
169 | } | ||
170 | |||
171 | /* compute true data size to be loaded */ | ||
172 | truesize = sp->v.size; | ||
173 | if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) | ||
174 | truesize += sp->v.loopend - sp->v.loopstart; | ||
175 | if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) | ||
176 | truesize += BLANK_LOOP_SIZE; | ||
177 | |||
178 | sp->block = snd_util_mem_alloc(hdr, truesize * 2); | ||
179 | if (sp->block == NULL) { | ||
180 | /*snd_printd("EMU8000: out of memory\n");*/ | ||
181 | /* not ENOMEM (for compatibility) */ | ||
182 | return -ENOSPC; | ||
183 | } | ||
184 | |||
185 | if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { | ||
186 | if (!access_ok(VERIFY_READ, data, sp->v.size)) | ||
187 | return -EFAULT; | ||
188 | } else { | ||
189 | if (!access_ok(VERIFY_READ, data, sp->v.size * 2)) | ||
190 | return -EFAULT; | ||
191 | } | ||
192 | |||
193 | /* recalculate address offset */ | ||
194 | sp->v.end -= sp->v.start; | ||
195 | sp->v.loopstart -= sp->v.start; | ||
196 | sp->v.loopend -= sp->v.start; | ||
197 | sp->v.start = 0; | ||
198 | |||
199 | /* dram position (in word) -- mem_offset is byte */ | ||
200 | dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1); | ||
201 | dram_start = dram_offset; | ||
202 | |||
203 | /* set the total size (store onto obsolete checksum value) */ | ||
204 | sp->v.truesize = truesize * 2; /* in bytes */ | ||
205 | |||
206 | snd_emux_terminate_all(emu->emu); | ||
207 | if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0) | ||
208 | return rc; | ||
209 | |||
210 | /* Set the address to start writing at */ | ||
211 | snd_emu8000_write_wait(emu); | ||
212 | EMU8000_SMALW_WRITE(emu, dram_offset); | ||
213 | |||
214 | /*snd_emu8000_init_fm(emu);*/ | ||
215 | |||
216 | #if 0 | ||
217 | /* first block - write 48 samples for silence */ | ||
218 | if (! sp->block->offset) { | ||
219 | for (i = 0; i < BLANK_HEAD_SIZE; i++) { | ||
220 | write_word(emu, &dram_offset, 0); | ||
221 | } | ||
222 | } | ||
223 | #endif | ||
224 | |||
225 | offset = 0; | ||
226 | for (i = 0; i < sp->v.size; i++) { | ||
227 | unsigned short s; | ||
228 | |||
229 | s = read_word(data, offset, sp->v.mode_flags); | ||
230 | offset++; | ||
231 | write_word(emu, &dram_offset, s); | ||
232 | |||
233 | /* we may take too long time in this loop. | ||
234 | * so give controls back to kernel if needed. | ||
235 | */ | ||
236 | cond_resched(); | ||
237 | |||
238 | if (i == sp->v.loopend && | ||
239 | (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))) | ||
240 | { | ||
241 | int looplen = sp->v.loopend - sp->v.loopstart; | ||
242 | int k; | ||
243 | |||
244 | /* copy reverse loop */ | ||
245 | for (k = 1; k <= looplen; k++) { | ||
246 | s = read_word(data, offset - k, sp->v.mode_flags); | ||
247 | write_word(emu, &dram_offset, s); | ||
248 | } | ||
249 | if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { | ||
250 | sp->v.loopend += looplen; | ||
251 | } else { | ||
252 | sp->v.loopstart += looplen; | ||
253 | sp->v.loopend += looplen; | ||
254 | } | ||
255 | sp->v.end += looplen; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | /* if no blank loop is attached in the sample, add it */ | ||
260 | if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { | ||
261 | for (i = 0; i < BLANK_LOOP_SIZE; i++) { | ||
262 | write_word(emu, &dram_offset, 0); | ||
263 | } | ||
264 | if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { | ||
265 | sp->v.loopstart = sp->v.end + BLANK_LOOP_START; | ||
266 | sp->v.loopend = sp->v.end + BLANK_LOOP_END; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | /* add dram offset */ | ||
271 | sp->v.start += dram_start; | ||
272 | sp->v.end += dram_start; | ||
273 | sp->v.loopstart += dram_start; | ||
274 | sp->v.loopend += dram_start; | ||
275 | |||
276 | snd_emu8000_close_dma(emu); | ||
277 | snd_emu8000_init_fm(emu); | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * free a sample block | ||
284 | */ | ||
285 | int | ||
286 | snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr) | ||
287 | { | ||
288 | if (sp->block) { | ||
289 | snd_util_mem_free(hdr, sp->block); | ||
290 | sp->block = NULL; | ||
291 | } | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | |||
296 | /* | ||
297 | * sample_reset callback - terminate voices | ||
298 | */ | ||
299 | void | ||
300 | snd_emu8000_sample_reset(snd_emux_t *rec) | ||
301 | { | ||
302 | snd_emux_terminate_all(rec); | ||
303 | } | ||
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c new file mode 100644 index 000000000000..db5eb8b55058 --- /dev/null +++ b/sound/isa/sb/emu8000_pcm.c | |||
@@ -0,0 +1,704 @@ | |||
1 | /* | ||
2 | * pcm emulation on emu8000 wavetable | ||
3 | * | ||
4 | * Copyright (C) 2002 Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include "emu8000_local.h" | ||
22 | #include <linux/init.h> | ||
23 | #include <sound/initval.h> | ||
24 | #include <sound/pcm.h> | ||
25 | |||
26 | /* | ||
27 | * define the following if you want to use this pcm with non-interleaved mode | ||
28 | */ | ||
29 | /* #define USE_NONINTERLEAVE */ | ||
30 | |||
31 | /* NOTE: for using the non-interleaved mode with alsa-lib, you have to set | ||
32 | * mmap_emulation flag to 1 in your .asoundrc, such like | ||
33 | * | ||
34 | * pcm.emu8k { | ||
35 | * type plug | ||
36 | * slave.pcm { | ||
37 | * type hw | ||
38 | * card 0 | ||
39 | * device 1 | ||
40 | * mmap_emulation 1 | ||
41 | * } | ||
42 | * } | ||
43 | * | ||
44 | * besides, for the time being, the non-interleaved mode doesn't work well on | ||
45 | * alsa-lib... | ||
46 | */ | ||
47 | |||
48 | |||
49 | typedef struct snd_emu8k_pcm emu8k_pcm_t; | ||
50 | |||
51 | struct snd_emu8k_pcm { | ||
52 | emu8000_t *emu; | ||
53 | snd_pcm_substream_t *substream; | ||
54 | |||
55 | unsigned int allocated_bytes; | ||
56 | snd_util_memblk_t *block; | ||
57 | unsigned int offset; | ||
58 | unsigned int buf_size; | ||
59 | unsigned int period_size; | ||
60 | unsigned int loop_start[2]; | ||
61 | unsigned int pitch; | ||
62 | int panning[2]; | ||
63 | int last_ptr; | ||
64 | int period_pos; | ||
65 | int voices; | ||
66 | unsigned int dram_opened: 1; | ||
67 | unsigned int running: 1; | ||
68 | unsigned int timer_running: 1; | ||
69 | struct timer_list timer; | ||
70 | spinlock_t timer_lock; | ||
71 | }; | ||
72 | |||
73 | #define LOOP_BLANK_SIZE 8 | ||
74 | |||
75 | |||
76 | /* | ||
77 | * open up channels for the simultaneous data transfer and playback | ||
78 | */ | ||
79 | static int | ||
80 | emu8k_open_dram_for_pcm(emu8000_t *emu, int channels) | ||
81 | { | ||
82 | int i; | ||
83 | |||
84 | /* reserve up to 2 voices for playback */ | ||
85 | snd_emux_lock_voice(emu->emu, 0); | ||
86 | if (channels > 1) | ||
87 | snd_emux_lock_voice(emu->emu, 1); | ||
88 | |||
89 | /* reserve 28 voices for loading */ | ||
90 | for (i = channels + 1; i < EMU8000_DRAM_VOICES; i++) { | ||
91 | unsigned int mode = EMU8000_RAM_WRITE; | ||
92 | snd_emux_lock_voice(emu->emu, i); | ||
93 | #ifndef USE_NONINTERLEAVE | ||
94 | if (channels > 1 && (i & 1) != 0) | ||
95 | mode |= EMU8000_RAM_RIGHT; | ||
96 | #endif | ||
97 | snd_emu8000_dma_chan(emu, i, mode); | ||
98 | } | ||
99 | |||
100 | /* assign voice 31 and 32 to ROM */ | ||
101 | EMU8000_VTFT_WRITE(emu, 30, 0); | ||
102 | EMU8000_PSST_WRITE(emu, 30, 0x1d8); | ||
103 | EMU8000_CSL_WRITE(emu, 30, 0x1e0); | ||
104 | EMU8000_CCCA_WRITE(emu, 30, 0x1d8); | ||
105 | EMU8000_VTFT_WRITE(emu, 31, 0); | ||
106 | EMU8000_PSST_WRITE(emu, 31, 0x1d8); | ||
107 | EMU8000_CSL_WRITE(emu, 31, 0x1e0); | ||
108 | EMU8000_CCCA_WRITE(emu, 31, 0x1d8); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | */ | ||
115 | static void | ||
116 | snd_emu8000_write_wait(emu8000_t *emu, int can_schedule) | ||
117 | { | ||
118 | while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { | ||
119 | if (can_schedule) { | ||
120 | set_current_state(TASK_INTERRUPTIBLE); | ||
121 | schedule_timeout(1); | ||
122 | if (signal_pending(current)) | ||
123 | break; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * close all channels | ||
130 | */ | ||
131 | static void | ||
132 | emu8k_close_dram(emu8000_t *emu) | ||
133 | { | ||
134 | int i; | ||
135 | |||
136 | for (i = 0; i < 2; i++) | ||
137 | snd_emux_unlock_voice(emu->emu, i); | ||
138 | for (; i < EMU8000_DRAM_VOICES; i++) { | ||
139 | snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); | ||
140 | snd_emux_unlock_voice(emu->emu, i); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * convert Hz to AWE32 rate offset (see emux/soundfont.c) | ||
146 | */ | ||
147 | |||
148 | #define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ | ||
149 | #define SAMPLERATE_RATIO 4096 | ||
150 | |||
151 | static int calc_rate_offset(int hz) | ||
152 | { | ||
153 | return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); | ||
154 | } | ||
155 | |||
156 | |||
157 | /* | ||
158 | */ | ||
159 | |||
160 | static snd_pcm_hardware_t emu8k_pcm_hw = { | ||
161 | #ifdef USE_NONINTERLEAVE | ||
162 | .info = SNDRV_PCM_INFO_NONINTERLEAVED, | ||
163 | #else | ||
164 | .info = SNDRV_PCM_INFO_INTERLEAVED, | ||
165 | #endif | ||
166 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
167 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
168 | .rate_min = 4000, | ||
169 | .rate_max = 48000, | ||
170 | .channels_min = 1, | ||
171 | .channels_max = 2, | ||
172 | .buffer_bytes_max = (128*1024), | ||
173 | .period_bytes_min = 1024, | ||
174 | .period_bytes_max = (128*1024), | ||
175 | .periods_min = 2, | ||
176 | .periods_max = 1024, | ||
177 | .fifo_size = 0, | ||
178 | |||
179 | }; | ||
180 | |||
181 | /* | ||
182 | * get the current position at the given channel from CCCA register | ||
183 | */ | ||
184 | static inline int emu8k_get_curpos(emu8k_pcm_t *rec, int ch) | ||
185 | { | ||
186 | int val = EMU8000_CCCA_READ(rec->emu, ch) & 0xfffffff; | ||
187 | val -= rec->loop_start[ch] - 1; | ||
188 | return val; | ||
189 | } | ||
190 | |||
191 | |||
192 | /* | ||
193 | * timer interrupt handler | ||
194 | * check the current position and update the period if necessary. | ||
195 | */ | ||
196 | static void emu8k_pcm_timer_func(unsigned long data) | ||
197 | { | ||
198 | emu8k_pcm_t *rec = (emu8k_pcm_t *)data; | ||
199 | int ptr, delta; | ||
200 | |||
201 | spin_lock(&rec->timer_lock); | ||
202 | /* update the current pointer */ | ||
203 | ptr = emu8k_get_curpos(rec, 0); | ||
204 | if (ptr < rec->last_ptr) | ||
205 | delta = ptr + rec->buf_size - rec->last_ptr; | ||
206 | else | ||
207 | delta = ptr - rec->last_ptr; | ||
208 | rec->period_pos += delta; | ||
209 | rec->last_ptr = ptr; | ||
210 | |||
211 | /* reprogram timer */ | ||
212 | rec->timer.expires = jiffies + 1; | ||
213 | add_timer(&rec->timer); | ||
214 | |||
215 | /* update period */ | ||
216 | if (rec->period_pos >= (int)rec->period_size) { | ||
217 | rec->period_pos %= rec->period_size; | ||
218 | spin_unlock(&rec->timer_lock); | ||
219 | snd_pcm_period_elapsed(rec->substream); | ||
220 | return; | ||
221 | } | ||
222 | spin_unlock(&rec->timer_lock); | ||
223 | } | ||
224 | |||
225 | |||
226 | /* | ||
227 | * open pcm | ||
228 | * creating an instance here | ||
229 | */ | ||
230 | static int emu8k_pcm_open(snd_pcm_substream_t *subs) | ||
231 | { | ||
232 | emu8000_t *emu = snd_pcm_substream_chip(subs); | ||
233 | emu8k_pcm_t *rec; | ||
234 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
235 | |||
236 | rec = kcalloc(1, sizeof(*rec), GFP_KERNEL); | ||
237 | if (! rec) | ||
238 | return -ENOMEM; | ||
239 | |||
240 | rec->emu = emu; | ||
241 | rec->substream = subs; | ||
242 | runtime->private_data = rec; | ||
243 | |||
244 | spin_lock_init(&rec->timer_lock); | ||
245 | init_timer(&rec->timer); | ||
246 | rec->timer.function = emu8k_pcm_timer_func; | ||
247 | rec->timer.data = (unsigned long)rec; | ||
248 | |||
249 | runtime->hw = emu8k_pcm_hw; | ||
250 | runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3; | ||
251 | runtime->hw.period_bytes_max = runtime->hw.buffer_bytes_max / 2; | ||
252 | |||
253 | /* use timer to update periods.. (specified in msec) */ | ||
254 | snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, | ||
255 | (1000000 + HZ - 1) / HZ, UINT_MAX); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int emu8k_pcm_close(snd_pcm_substream_t *subs) | ||
261 | { | ||
262 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
263 | kfree(rec); | ||
264 | subs->runtime->private_data = NULL; | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * calculate pitch target | ||
270 | */ | ||
271 | static int calc_pitch_target(int pitch) | ||
272 | { | ||
273 | int ptarget = 1 << (pitch >> 12); | ||
274 | if (pitch & 0x800) ptarget += (ptarget * 0x102e) / 0x2710; | ||
275 | if (pitch & 0x400) ptarget += (ptarget * 0x764) / 0x2710; | ||
276 | if (pitch & 0x200) ptarget += (ptarget * 0x389) / 0x2710; | ||
277 | ptarget += (ptarget >> 1); | ||
278 | if (ptarget > 0xffff) ptarget = 0xffff; | ||
279 | return ptarget; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * set up the voice | ||
284 | */ | ||
285 | static void setup_voice(emu8k_pcm_t *rec, int ch) | ||
286 | { | ||
287 | emu8000_t *hw = rec->emu; | ||
288 | unsigned int temp; | ||
289 | |||
290 | /* channel to be silent and idle */ | ||
291 | EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); | ||
292 | EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); | ||
293 | EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); | ||
294 | EMU8000_PTRX_WRITE(hw, ch, 0); | ||
295 | EMU8000_CPF_WRITE(hw, ch, 0); | ||
296 | |||
297 | /* pitch offset */ | ||
298 | EMU8000_IP_WRITE(hw, ch, rec->pitch); | ||
299 | /* set envelope parameters */ | ||
300 | EMU8000_ENVVAL_WRITE(hw, ch, 0x8000); | ||
301 | EMU8000_ATKHLD_WRITE(hw, ch, 0x7f7f); | ||
302 | EMU8000_DCYSUS_WRITE(hw, ch, 0x7f7f); | ||
303 | EMU8000_ENVVOL_WRITE(hw, ch, 0x8000); | ||
304 | EMU8000_ATKHLDV_WRITE(hw, ch, 0x7f7f); | ||
305 | /* decay/sustain parameter for volume envelope is used | ||
306 | for triggerg the voice */ | ||
307 | /* modulation envelope heights */ | ||
308 | EMU8000_PEFE_WRITE(hw, ch, 0x0); | ||
309 | /* lfo1/2 delay */ | ||
310 | EMU8000_LFO1VAL_WRITE(hw, ch, 0x8000); | ||
311 | EMU8000_LFO2VAL_WRITE(hw, ch, 0x8000); | ||
312 | /* lfo1 pitch & cutoff shift */ | ||
313 | EMU8000_FMMOD_WRITE(hw, ch, 0); | ||
314 | /* lfo1 volume & freq */ | ||
315 | EMU8000_TREMFRQ_WRITE(hw, ch, 0); | ||
316 | /* lfo2 pitch & freq */ | ||
317 | EMU8000_FM2FRQ2_WRITE(hw, ch, 0); | ||
318 | /* pan & loop start */ | ||
319 | temp = rec->panning[ch]; | ||
320 | temp = (temp <<24) | ((unsigned int)rec->loop_start[ch] - 1); | ||
321 | EMU8000_PSST_WRITE(hw, ch, temp); | ||
322 | /* chorus & loop end (chorus 8bit, MSB) */ | ||
323 | temp = 0; // chorus | ||
324 | temp = (temp << 24) | ((unsigned int)rec->loop_start[ch] + rec->buf_size - 1); | ||
325 | EMU8000_CSL_WRITE(hw, ch, temp); | ||
326 | /* Q & current address (Q 4bit value, MSB) */ | ||
327 | temp = 0; // filterQ | ||
328 | temp = (temp << 28) | ((unsigned int)rec->loop_start[ch] - 1); | ||
329 | EMU8000_CCCA_WRITE(hw, ch, temp); | ||
330 | /* clear unknown registers */ | ||
331 | EMU8000_00A0_WRITE(hw, ch, 0); | ||
332 | EMU8000_0080_WRITE(hw, ch, 0); | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * trigger the voice | ||
337 | */ | ||
338 | static void start_voice(emu8k_pcm_t *rec, int ch) | ||
339 | { | ||
340 | unsigned long flags; | ||
341 | emu8000_t *hw = rec->emu; | ||
342 | unsigned int temp, aux; | ||
343 | int pt = calc_pitch_target(rec->pitch); | ||
344 | |||
345 | /* cutoff and volume */ | ||
346 | EMU8000_IFATN_WRITE(hw, ch, 0xff00); | ||
347 | EMU8000_VTFT_WRITE(hw, ch, 0xffff); | ||
348 | EMU8000_CVCF_WRITE(hw, ch, 0xffff); | ||
349 | /* trigger envelope */ | ||
350 | EMU8000_DCYSUSV_WRITE(hw, ch, 0x7f7f); | ||
351 | /* set reverb and pitch target */ | ||
352 | temp = 0; // reverb | ||
353 | if (rec->panning[ch] == 0) | ||
354 | aux = 0xff; | ||
355 | else | ||
356 | aux = (-rec->panning[ch]) & 0xff; | ||
357 | temp = (temp << 8) | (pt << 16) | aux; | ||
358 | EMU8000_PTRX_WRITE(hw, ch, temp); | ||
359 | EMU8000_CPF_WRITE(hw, ch, pt << 16); | ||
360 | |||
361 | /* start timer */ | ||
362 | spin_lock_irqsave(&rec->timer_lock, flags); | ||
363 | if (! rec->timer_running) { | ||
364 | rec->timer.expires = jiffies + 1; | ||
365 | add_timer(&rec->timer); | ||
366 | rec->timer_running = 1; | ||
367 | } | ||
368 | spin_unlock_irqrestore(&rec->timer_lock, flags); | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * stop the voice immediately | ||
373 | */ | ||
374 | static void stop_voice(emu8k_pcm_t *rec, int ch) | ||
375 | { | ||
376 | unsigned long flags; | ||
377 | emu8000_t *hw = rec->emu; | ||
378 | |||
379 | EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); | ||
380 | |||
381 | /* stop timer */ | ||
382 | spin_lock_irqsave(&rec->timer_lock, flags); | ||
383 | if (rec->timer_running) { | ||
384 | del_timer(&rec->timer); | ||
385 | rec->timer_running = 0; | ||
386 | } | ||
387 | spin_unlock_irqrestore(&rec->timer_lock, flags); | ||
388 | } | ||
389 | |||
390 | static int emu8k_pcm_trigger(snd_pcm_substream_t *subs, int cmd) | ||
391 | { | ||
392 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
393 | int ch; | ||
394 | |||
395 | switch (cmd) { | ||
396 | case SNDRV_PCM_TRIGGER_START: | ||
397 | for (ch = 0; ch < rec->voices; ch++) | ||
398 | start_voice(rec, ch); | ||
399 | rec->running = 1; | ||
400 | break; | ||
401 | case SNDRV_PCM_TRIGGER_STOP: | ||
402 | rec->running = 0; | ||
403 | for (ch = 0; ch < rec->voices; ch++) | ||
404 | stop_voice(rec, ch); | ||
405 | break; | ||
406 | default: | ||
407 | return -EINVAL; | ||
408 | } | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | |||
413 | /* | ||
414 | * copy / silence ops | ||
415 | */ | ||
416 | |||
417 | /* | ||
418 | * this macro should be inserted in the copy/silence loops | ||
419 | * to reduce the latency. without this, the system will hang up | ||
420 | * during the whole loop. | ||
421 | */ | ||
422 | #define CHECK_SCHEDULER() \ | ||
423 | do { \ | ||
424 | cond_resched();\ | ||
425 | if (signal_pending(current))\ | ||
426 | return -EAGAIN;\ | ||
427 | } while (0) | ||
428 | |||
429 | |||
430 | #ifdef USE_NONINTERLEAVE | ||
431 | /* copy one channel block */ | ||
432 | static int emu8k_transfer_block(emu8000_t *emu, int offset, unsigned short *buf, int count) | ||
433 | { | ||
434 | EMU8000_SMALW_WRITE(emu, offset); | ||
435 | while (count > 0) { | ||
436 | unsigned short sval; | ||
437 | CHECK_SCHEDULER(); | ||
438 | get_user(sval, buf); | ||
439 | EMU8000_SMLD_WRITE(emu, sval); | ||
440 | buf++; | ||
441 | count--; | ||
442 | } | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static int emu8k_pcm_copy(snd_pcm_substream_t *subs, | ||
447 | int voice, | ||
448 | snd_pcm_uframes_t pos, | ||
449 | void *src, | ||
450 | snd_pcm_uframes_t count) | ||
451 | { | ||
452 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
453 | emu8000_t *emu = rec->emu; | ||
454 | |||
455 | snd_emu8000_write_wait(emu, 1); | ||
456 | if (voice == -1) { | ||
457 | unsigned short *buf = src; | ||
458 | int i, err; | ||
459 | count /= rec->voices; | ||
460 | for (i = 0; i < rec->voices; i++) { | ||
461 | err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count); | ||
462 | if (err < 0) | ||
463 | return err; | ||
464 | buf += count; | ||
465 | } | ||
466 | return 0; | ||
467 | } else { | ||
468 | return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | /* make a channel block silence */ | ||
473 | static int emu8k_silence_block(emu8000_t *emu, int offset, int count) | ||
474 | { | ||
475 | EMU8000_SMALW_WRITE(emu, offset); | ||
476 | while (count > 0) { | ||
477 | CHECK_SCHEDULER(); | ||
478 | EMU8000_SMLD_WRITE(emu, 0); | ||
479 | count--; | ||
480 | } | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | static int emu8k_pcm_silence(snd_pcm_substream_t *subs, | ||
485 | int voice, | ||
486 | snd_pcm_uframes_t pos, | ||
487 | snd_pcm_uframes_t count) | ||
488 | { | ||
489 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
490 | emu8000_t *emu = rec->emu; | ||
491 | |||
492 | snd_emu8000_write_wait(emu, 1); | ||
493 | if (voice == -1 && rec->voices == 1) | ||
494 | voice = 0; | ||
495 | if (voice == -1) { | ||
496 | int err; | ||
497 | err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2); | ||
498 | if (err < 0) | ||
499 | return err; | ||
500 | return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2); | ||
501 | } else { | ||
502 | return emu8k_silence_block(emu, pos + rec->loop_start[voice], count); | ||
503 | } | ||
504 | } | ||
505 | |||
506 | #else /* interleave */ | ||
507 | |||
508 | /* | ||
509 | * copy the interleaved data can be done easily by using | ||
510 | * DMA "left" and "right" channels on emu8k engine. | ||
511 | */ | ||
512 | static int emu8k_pcm_copy(snd_pcm_substream_t *subs, | ||
513 | int voice, | ||
514 | snd_pcm_uframes_t pos, | ||
515 | void __user *src, | ||
516 | snd_pcm_uframes_t count) | ||
517 | { | ||
518 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
519 | emu8000_t *emu = rec->emu; | ||
520 | unsigned short __user *buf = src; | ||
521 | |||
522 | snd_emu8000_write_wait(emu, 1); | ||
523 | EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); | ||
524 | if (rec->voices > 1) | ||
525 | EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); | ||
526 | |||
527 | while (count-- > 0) { | ||
528 | unsigned short sval; | ||
529 | CHECK_SCHEDULER(); | ||
530 | get_user(sval, buf); | ||
531 | EMU8000_SMLD_WRITE(emu, sval); | ||
532 | buf++; | ||
533 | if (rec->voices > 1) { | ||
534 | CHECK_SCHEDULER(); | ||
535 | get_user(sval, buf); | ||
536 | EMU8000_SMRD_WRITE(emu, sval); | ||
537 | buf++; | ||
538 | } | ||
539 | } | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static int emu8k_pcm_silence(snd_pcm_substream_t *subs, | ||
544 | int voice, | ||
545 | snd_pcm_uframes_t pos, | ||
546 | snd_pcm_uframes_t count) | ||
547 | { | ||
548 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
549 | emu8000_t *emu = rec->emu; | ||
550 | |||
551 | snd_emu8000_write_wait(emu, 1); | ||
552 | EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos); | ||
553 | if (rec->voices > 1) | ||
554 | EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos); | ||
555 | while (count-- > 0) { | ||
556 | CHECK_SCHEDULER(); | ||
557 | EMU8000_SMLD_WRITE(emu, 0); | ||
558 | if (rec->voices > 1) { | ||
559 | CHECK_SCHEDULER(); | ||
560 | EMU8000_SMRD_WRITE(emu, 0); | ||
561 | } | ||
562 | } | ||
563 | return 0; | ||
564 | } | ||
565 | #endif | ||
566 | |||
567 | |||
568 | /* | ||
569 | * allocate a memory block | ||
570 | */ | ||
571 | static int emu8k_pcm_hw_params(snd_pcm_substream_t *subs, | ||
572 | snd_pcm_hw_params_t *hw_params) | ||
573 | { | ||
574 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
575 | |||
576 | if (rec->block) { | ||
577 | /* reallocation - release the old block */ | ||
578 | snd_util_mem_free(rec->emu->memhdr, rec->block); | ||
579 | rec->block = NULL; | ||
580 | } | ||
581 | |||
582 | rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4; | ||
583 | rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes); | ||
584 | if (! rec->block) | ||
585 | return -ENOMEM; | ||
586 | rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */ | ||
587 | /* at least dma_bytes must be set for non-interleaved mode */ | ||
588 | subs->dma_buffer.bytes = params_buffer_bytes(hw_params); | ||
589 | |||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | /* | ||
594 | * free the memory block | ||
595 | */ | ||
596 | static int emu8k_pcm_hw_free(snd_pcm_substream_t *subs) | ||
597 | { | ||
598 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
599 | |||
600 | if (rec->block) { | ||
601 | int ch; | ||
602 | for (ch = 0; ch < rec->voices; ch++) | ||
603 | stop_voice(rec, ch); // to be sure | ||
604 | if (rec->dram_opened) | ||
605 | emu8k_close_dram(rec->emu); | ||
606 | snd_util_mem_free(rec->emu->memhdr, rec->block); | ||
607 | rec->block = NULL; | ||
608 | } | ||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | */ | ||
614 | static int emu8k_pcm_prepare(snd_pcm_substream_t *subs) | ||
615 | { | ||
616 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
617 | |||
618 | rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate); | ||
619 | rec->last_ptr = 0; | ||
620 | rec->period_pos = 0; | ||
621 | |||
622 | rec->buf_size = subs->runtime->buffer_size; | ||
623 | rec->period_size = subs->runtime->period_size; | ||
624 | rec->voices = subs->runtime->channels; | ||
625 | rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE; | ||
626 | if (rec->voices > 1) | ||
627 | rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE; | ||
628 | if (rec->voices > 1) { | ||
629 | rec->panning[0] = 0xff; | ||
630 | rec->panning[1] = 0x00; | ||
631 | } else | ||
632 | rec->panning[0] = 0x80; | ||
633 | |||
634 | if (! rec->dram_opened) { | ||
635 | int err, i, ch; | ||
636 | |||
637 | snd_emux_terminate_all(rec->emu->emu); | ||
638 | if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0) | ||
639 | return err; | ||
640 | rec->dram_opened = 1; | ||
641 | |||
642 | /* clear loop blanks */ | ||
643 | snd_emu8000_write_wait(rec->emu, 0); | ||
644 | EMU8000_SMALW_WRITE(rec->emu, rec->offset); | ||
645 | for (i = 0; i < LOOP_BLANK_SIZE; i++) | ||
646 | EMU8000_SMLD_WRITE(rec->emu, 0); | ||
647 | for (ch = 0; ch < rec->voices; ch++) { | ||
648 | EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size); | ||
649 | for (i = 0; i < LOOP_BLANK_SIZE; i++) | ||
650 | EMU8000_SMLD_WRITE(rec->emu, 0); | ||
651 | } | ||
652 | } | ||
653 | |||
654 | setup_voice(rec, 0); | ||
655 | if (rec->voices > 1) | ||
656 | setup_voice(rec, 1); | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static snd_pcm_uframes_t emu8k_pcm_pointer(snd_pcm_substream_t *subs) | ||
661 | { | ||
662 | emu8k_pcm_t *rec = subs->runtime->private_data; | ||
663 | if (rec->running) | ||
664 | return emu8k_get_curpos(rec, 0); | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | |||
669 | static snd_pcm_ops_t emu8k_pcm_ops = { | ||
670 | .open = emu8k_pcm_open, | ||
671 | .close = emu8k_pcm_close, | ||
672 | .ioctl = snd_pcm_lib_ioctl, | ||
673 | .hw_params = emu8k_pcm_hw_params, | ||
674 | .hw_free = emu8k_pcm_hw_free, | ||
675 | .prepare = emu8k_pcm_prepare, | ||
676 | .trigger = emu8k_pcm_trigger, | ||
677 | .pointer = emu8k_pcm_pointer, | ||
678 | .copy = emu8k_pcm_copy, | ||
679 | .silence = emu8k_pcm_silence, | ||
680 | }; | ||
681 | |||
682 | |||
683 | static void snd_emu8000_pcm_free(snd_pcm_t *pcm) | ||
684 | { | ||
685 | emu8000_t *emu = pcm->private_data; | ||
686 | emu->pcm = NULL; | ||
687 | } | ||
688 | |||
689 | int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index) | ||
690 | { | ||
691 | snd_pcm_t *pcm; | ||
692 | int err; | ||
693 | |||
694 | if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0) | ||
695 | return err; | ||
696 | pcm->private_data = emu; | ||
697 | pcm->private_free = snd_emu8000_pcm_free; | ||
698 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops); | ||
699 | emu->pcm = pcm; | ||
700 | |||
701 | snd_device_register(card, pcm); | ||
702 | |||
703 | return 0; | ||
704 | } | ||
diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c new file mode 100644 index 000000000000..1f63aa52d596 --- /dev/null +++ b/sound/isa/sb/emu8000_synth.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk> | ||
4 | * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * Emu8000 synth plug-in routine | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include "emu8000_local.h" | ||
24 | #include <linux/init.h> | ||
25 | #include <sound/initval.h> | ||
26 | |||
27 | MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); | ||
28 | MODULE_DESCRIPTION("Emu8000 synth plug-in routine"); | ||
29 | MODULE_LICENSE("GPL"); | ||
30 | |||
31 | /*----------------------------------------------------------------*/ | ||
32 | |||
33 | /* | ||
34 | * create a new hardware dependent device for Emu8000 | ||
35 | */ | ||
36 | static int snd_emu8000_new_device(snd_seq_device_t *dev) | ||
37 | { | ||
38 | emu8000_t *hw; | ||
39 | snd_emux_t *emu; | ||
40 | |||
41 | hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
42 | if (hw == NULL) | ||
43 | return -EINVAL; | ||
44 | |||
45 | if (hw->emu) | ||
46 | return -EBUSY; /* already exists..? */ | ||
47 | |||
48 | if (snd_emux_new(&emu) < 0) | ||
49 | return -ENOMEM; | ||
50 | |||
51 | hw->emu = emu; | ||
52 | snd_emu8000_ops_setup(hw); | ||
53 | |||
54 | emu->hw = hw; | ||
55 | emu->max_voices = EMU8000_DRAM_VOICES; | ||
56 | emu->num_ports = hw->seq_ports; | ||
57 | |||
58 | if (hw->memhdr) { | ||
59 | snd_printk("memhdr is already initialized!?\n"); | ||
60 | snd_util_memhdr_free(hw->memhdr); | ||
61 | } | ||
62 | hw->memhdr = snd_util_memhdr_new(hw->mem_size); | ||
63 | if (hw->memhdr == NULL) { | ||
64 | snd_emux_free(emu); | ||
65 | hw->emu = NULL; | ||
66 | return -ENOMEM; | ||
67 | } | ||
68 | |||
69 | emu->memhdr = hw->memhdr; | ||
70 | emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */ | ||
71 | emu->midi_devidx = 1; | ||
72 | emu->linear_panning = 1; | ||
73 | emu->hwdep_idx = 2; /* FIXED */ | ||
74 | |||
75 | if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) { | ||
76 | snd_emux_free(emu); | ||
77 | snd_util_memhdr_free(hw->memhdr); | ||
78 | hw->emu = NULL; | ||
79 | hw->memhdr = NULL; | ||
80 | return -ENOMEM; | ||
81 | } | ||
82 | |||
83 | if (hw->mem_size > 0) | ||
84 | snd_emu8000_pcm_new(dev->card, hw, 1); | ||
85 | |||
86 | dev->driver_data = hw; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | |||
92 | /* | ||
93 | * free all resources | ||
94 | */ | ||
95 | static int snd_emu8000_delete_device(snd_seq_device_t *dev) | ||
96 | { | ||
97 | emu8000_t *hw; | ||
98 | |||
99 | if (dev->driver_data == NULL) | ||
100 | return 0; /* no synth was allocated actually */ | ||
101 | |||
102 | hw = dev->driver_data; | ||
103 | if (hw->pcm) | ||
104 | snd_device_free(dev->card, hw->pcm); | ||
105 | if (hw->emu) | ||
106 | snd_emux_free(hw->emu); | ||
107 | if (hw->memhdr) | ||
108 | snd_util_memhdr_free(hw->memhdr); | ||
109 | hw->emu = NULL; | ||
110 | hw->memhdr = NULL; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * INIT part | ||
116 | */ | ||
117 | |||
118 | static int __init alsa_emu8000_init(void) | ||
119 | { | ||
120 | |||
121 | static snd_seq_dev_ops_t ops = { | ||
122 | snd_emu8000_new_device, | ||
123 | snd_emu8000_delete_device, | ||
124 | }; | ||
125 | return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*)); | ||
126 | } | ||
127 | |||
128 | static void __exit alsa_emu8000_exit(void) | ||
129 | { | ||
130 | snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); | ||
131 | } | ||
132 | |||
133 | module_init(alsa_emu8000_init) | ||
134 | module_exit(alsa_emu8000_exit) | ||
diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c new file mode 100644 index 000000000000..c859917c14db --- /dev/null +++ b/sound/isa/sb/es968.c | |||
@@ -0,0 +1,235 @@ | |||
1 | |||
2 | /* | ||
3 | card-es968.c - driver for ESS AudioDrive ES968 based soundcards. | ||
4 | Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it> | ||
5 | |||
6 | Thanks to Pierfrancesco 'qM2' Passerini. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2 of the License, or | ||
11 | (at your option) any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; if not, write to the Free Software | ||
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/pnp.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/initval.h> | ||
30 | #include <sound/sb.h> | ||
31 | |||
32 | #define PFX "es968: " | ||
33 | |||
34 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
35 | MODULE_DESCRIPTION("ESS AudioDrive ES968"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | MODULE_SUPPORTED_DEVICE("{{ESS,AudioDrive ES968}}"); | ||
38 | |||
39 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
40 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
41 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
42 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
43 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ | ||
44 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ | ||
45 | |||
46 | module_param_array(index, int, NULL, 0444); | ||
47 | MODULE_PARM_DESC(index, "Index value for es968 based soundcard."); | ||
48 | module_param_array(id, charp, NULL, 0444); | ||
49 | MODULE_PARM_DESC(id, "ID string for es968 based soundcard."); | ||
50 | module_param_array(enable, bool, NULL, 0444); | ||
51 | MODULE_PARM_DESC(enable, "Enable es968 based soundcard."); | ||
52 | module_param_array(port, long, NULL, 0444); | ||
53 | MODULE_PARM_DESC(port, "Port # for es968 driver."); | ||
54 | module_param_array(irq, int, NULL, 0444); | ||
55 | MODULE_PARM_DESC(irq, "IRQ # for es968 driver."); | ||
56 | module_param_array(dma8, int, NULL, 0444); | ||
57 | MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver."); | ||
58 | |||
59 | struct snd_card_es968 { | ||
60 | struct pnp_dev *dev; | ||
61 | }; | ||
62 | |||
63 | static struct pnp_card_device_id snd_es968_pnpids[] = { | ||
64 | { .id = "ESS0968", .devs = { { "@@@0968" }, } }, | ||
65 | { .id = "", } /* end */ | ||
66 | }; | ||
67 | |||
68 | MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids); | ||
69 | |||
70 | #define DRIVER_NAME "snd-card-es968" | ||
71 | |||
72 | static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id, | ||
73 | struct pt_regs *regs) | ||
74 | { | ||
75 | sb_t *chip = dev_id; | ||
76 | |||
77 | if (chip->open & SB_OPEN_PCM) { | ||
78 | return snd_sb8dsp_interrupt(chip); | ||
79 | } else { | ||
80 | return snd_sb8dsp_midi_interrupt(chip); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard, | ||
85 | struct pnp_card_link *card, | ||
86 | const struct pnp_card_device_id *id) | ||
87 | { | ||
88 | struct pnp_dev *pdev; | ||
89 | struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); | ||
90 | int err; | ||
91 | if (!cfg) | ||
92 | return -ENOMEM; | ||
93 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
94 | if (acard->dev == NULL) { | ||
95 | kfree(cfg); | ||
96 | return -ENODEV; | ||
97 | } | ||
98 | |||
99 | pdev = acard->dev; | ||
100 | |||
101 | pnp_init_resource_table(cfg); | ||
102 | |||
103 | /* override resources */ | ||
104 | if (port[dev] != SNDRV_AUTO_PORT) | ||
105 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
106 | if (dma8[dev] != SNDRV_AUTO_DMA) | ||
107 | pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); | ||
108 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
109 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
110 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
111 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
112 | err = pnp_activate_dev(pdev); | ||
113 | if (err < 0) { | ||
114 | snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); | ||
115 | kfree(cfg); | ||
116 | return err; | ||
117 | } | ||
118 | port[dev] = pnp_port_start(pdev, 0); | ||
119 | dma8[dev] = pnp_dma(pdev, 1); | ||
120 | irq[dev] = pnp_irq(pdev, 0); | ||
121 | |||
122 | kfree(cfg); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int __init snd_card_es968_probe(int dev, | ||
127 | struct pnp_card_link *pcard, | ||
128 | const struct pnp_card_device_id *pid) | ||
129 | { | ||
130 | int error; | ||
131 | sb_t *chip; | ||
132 | snd_card_t *card; | ||
133 | struct snd_card_es968 *acard; | ||
134 | |||
135 | if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
136 | sizeof(struct snd_card_es968))) == NULL) | ||
137 | return -ENOMEM; | ||
138 | acard = (struct snd_card_es968 *)card->private_data; | ||
139 | if ((error = snd_card_es968_pnp(dev, acard, pcard, pid))) { | ||
140 | snd_card_free(card); | ||
141 | return error; | ||
142 | } | ||
143 | snd_card_set_dev(card, &pcard->card->dev); | ||
144 | |||
145 | if ((error = snd_sbdsp_create(card, port[dev], | ||
146 | irq[dev], | ||
147 | snd_card_es968_interrupt, | ||
148 | dma8[dev], | ||
149 | -1, | ||
150 | SB_HW_AUTO, &chip)) < 0) { | ||
151 | snd_card_free(card); | ||
152 | return error; | ||
153 | } | ||
154 | |||
155 | if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { | ||
156 | snd_card_free(card); | ||
157 | return error; | ||
158 | } | ||
159 | |||
160 | if ((error = snd_sbmixer_new(chip)) < 0) { | ||
161 | snd_card_free(card); | ||
162 | return error; | ||
163 | } | ||
164 | |||
165 | if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { | ||
166 | snd_card_free(card); | ||
167 | return error; | ||
168 | } | ||
169 | |||
170 | strcpy(card->driver, "ES968"); | ||
171 | strcpy(card->shortname, "ESS ES968"); | ||
172 | sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", | ||
173 | card->shortname, chip->name, chip->port, irq[dev], dma8[dev]); | ||
174 | |||
175 | if ((error = snd_card_register(card)) < 0) { | ||
176 | snd_card_free(card); | ||
177 | return error; | ||
178 | } | ||
179 | pnp_set_card_drvdata(pcard, card); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card, | ||
184 | const struct pnp_card_device_id *id) | ||
185 | { | ||
186 | static int dev; | ||
187 | int res; | ||
188 | |||
189 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
190 | if (!enable[dev]) | ||
191 | continue; | ||
192 | res = snd_card_es968_probe(dev, card, id); | ||
193 | if (res < 0) | ||
194 | return res; | ||
195 | dev++; | ||
196 | return 0; | ||
197 | } | ||
198 | return -ENODEV; | ||
199 | } | ||
200 | |||
201 | static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard) | ||
202 | { | ||
203 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
204 | |||
205 | snd_card_disconnect(card); | ||
206 | snd_card_free_in_thread(card); | ||
207 | } | ||
208 | |||
209 | static struct pnp_card_driver es968_pnpc_driver = { | ||
210 | .flags = PNP_DRIVER_RES_DISABLE, | ||
211 | .name = "es968", | ||
212 | .id_table = snd_es968_pnpids, | ||
213 | .probe = snd_es968_pnp_detect, | ||
214 | .remove = __devexit_p(snd_es968_pnp_remove), | ||
215 | }; | ||
216 | |||
217 | static int __init alsa_card_es968_init(void) | ||
218 | { | ||
219 | int cards = pnp_register_card_driver(&es968_pnpc_driver); | ||
220 | #ifdef MODULE | ||
221 | if (cards == 0) { | ||
222 | pnp_unregister_card_driver(&es968_pnpc_driver); | ||
223 | snd_printk(KERN_ERR "no ES968 based soundcards found\n"); | ||
224 | } | ||
225 | #endif | ||
226 | return cards ? 0 : -ENODEV; | ||
227 | } | ||
228 | |||
229 | static void __exit alsa_card_es968_exit(void) | ||
230 | { | ||
231 | pnp_unregister_card_driver(&es968_pnpc_driver); | ||
232 | } | ||
233 | |||
234 | module_init(alsa_card_es968_init) | ||
235 | module_exit(alsa_card_es968_exit) | ||
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c new file mode 100644 index 000000000000..60e2c53c49fc --- /dev/null +++ b/sound/isa/sb/sb16.c | |||
@@ -0,0 +1,678 @@ | |||
1 | /* | ||
2 | * Driver for SoundBlaster 16/AWE32/AWE64 soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/pnp.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/sb.h> | ||
30 | #include <sound/sb16_csp.h> | ||
31 | #include <sound/mpu401.h> | ||
32 | #include <sound/opl3.h> | ||
33 | #include <sound/emu8000.h> | ||
34 | #include <sound/seq_device.h> | ||
35 | #define SNDRV_LEGACY_AUTO_PROBE | ||
36 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
37 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
38 | #include <sound/initval.h> | ||
39 | |||
40 | #ifdef SNDRV_SBAWE | ||
41 | #define PFX "sbawe: " | ||
42 | #else | ||
43 | #define PFX "sb16: " | ||
44 | #endif | ||
45 | |||
46 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
47 | MODULE_LICENSE("GPL"); | ||
48 | #ifndef SNDRV_SBAWE | ||
49 | MODULE_DESCRIPTION("Sound Blaster 16"); | ||
50 | MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 16}," | ||
51 | "{Creative Labs,SB Vibra16S}," | ||
52 | "{Creative Labs,SB Vibra16C}," | ||
53 | "{Creative Labs,SB Vibra16CL}," | ||
54 | "{Creative Labs,SB Vibra16X}}"); | ||
55 | #else | ||
56 | MODULE_DESCRIPTION("Sound Blaster AWE"); | ||
57 | MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB AWE 32}," | ||
58 | "{Creative Labs,SB AWE 64}," | ||
59 | "{Creative Labs,SB AWE 64 Gold}}"); | ||
60 | #endif | ||
61 | |||
62 | #if 0 | ||
63 | #define SNDRV_DEBUG_IRQ | ||
64 | #endif | ||
65 | |||
66 | #if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))) | ||
67 | #define SNDRV_SBAWE_EMU8000 | ||
68 | #endif | ||
69 | |||
70 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
71 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
72 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
73 | #ifdef CONFIG_PNP | ||
74 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
75 | #endif | ||
76 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ | ||
77 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x330,0x300 */ | ||
78 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
79 | #ifdef SNDRV_SBAWE_EMU8000 | ||
80 | static long awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
81 | #endif | ||
82 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
83 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
84 | static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */ | ||
85 | static int mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
86 | #ifdef CONFIG_SND_SB16_CSP | ||
87 | static int csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; | ||
88 | #endif | ||
89 | #ifdef SNDRV_SBAWE_EMU8000 | ||
90 | static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; | ||
91 | #endif | ||
92 | |||
93 | module_param_array(index, int, NULL, 0444); | ||
94 | MODULE_PARM_DESC(index, "Index value for SoundBlaster 16 soundcard."); | ||
95 | module_param_array(id, charp, NULL, 0444); | ||
96 | MODULE_PARM_DESC(id, "ID string for SoundBlaster 16 soundcard."); | ||
97 | module_param_array(enable, bool, NULL, 0444); | ||
98 | MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard."); | ||
99 | #ifdef CONFIG_PNP | ||
100 | module_param_array(isapnp, bool, NULL, 0444); | ||
101 | MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); | ||
102 | #endif | ||
103 | module_param_array(port, long, NULL, 0444); | ||
104 | MODULE_PARM_DESC(port, "Port # for SB16 driver."); | ||
105 | module_param_array(mpu_port, long, NULL, 0444); | ||
106 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver."); | ||
107 | module_param_array(fm_port, long, NULL, 0444); | ||
108 | MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver."); | ||
109 | #ifdef SNDRV_SBAWE_EMU8000 | ||
110 | module_param_array(awe_port, long, NULL, 0444); | ||
111 | MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver."); | ||
112 | #endif | ||
113 | module_param_array(irq, int, NULL, 0444); | ||
114 | MODULE_PARM_DESC(irq, "IRQ # for SB16 driver."); | ||
115 | module_param_array(dma8, int, NULL, 0444); | ||
116 | MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver."); | ||
117 | module_param_array(dma16, int, NULL, 0444); | ||
118 | MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver."); | ||
119 | module_param_array(mic_agc, int, NULL, 0444); | ||
120 | MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch."); | ||
121 | #ifdef CONFIG_SND_SB16_CSP | ||
122 | module_param_array(csp, int, NULL, 0444); | ||
123 | MODULE_PARM_DESC(csp, "ASP/CSP chip support."); | ||
124 | #endif | ||
125 | #ifdef SNDRV_SBAWE_EMU8000 | ||
126 | module_param_array(seq_ports, int, NULL, 0444); | ||
127 | MODULE_PARM_DESC(seq_ports, "Number of sequencer ports for WaveTable synth."); | ||
128 | #endif | ||
129 | |||
130 | struct snd_card_sb16 { | ||
131 | struct resource *fm_res; /* used to block FM i/o region for legacy cards */ | ||
132 | #ifdef CONFIG_PNP | ||
133 | int dev_no; | ||
134 | struct pnp_dev *dev; | ||
135 | #ifdef SNDRV_SBAWE_EMU8000 | ||
136 | struct pnp_dev *devwt; | ||
137 | #endif | ||
138 | #endif | ||
139 | }; | ||
140 | |||
141 | static snd_card_t *snd_sb16_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
142 | |||
143 | #ifdef CONFIG_PNP | ||
144 | |||
145 | static struct pnp_card_device_id snd_sb16_pnpids[] = { | ||
146 | #ifndef SNDRV_SBAWE | ||
147 | /* Sound Blaster 16 PnP */ | ||
148 | { .id = "CTL0024", .devs = { { "CTL0031" } } }, | ||
149 | /* Sound Blaster 16 PnP */ | ||
150 | { .id = "CTL0025", .devs = { { "CTL0031" } } }, | ||
151 | /* Sound Blaster 16 PnP */ | ||
152 | { .id = "CTL0026", .devs = { { "CTL0031" } } }, | ||
153 | /* Sound Blaster 16 PnP */ | ||
154 | { .id = "CTL0027", .devs = { { "CTL0031" } } }, | ||
155 | /* Sound Blaster 16 PnP */ | ||
156 | { .id = "CTL0028", .devs = { { "CTL0031" } } }, | ||
157 | /* Sound Blaster 16 PnP */ | ||
158 | { .id = "CTL0029", .devs = { { "CTL0031" } } }, | ||
159 | /* Sound Blaster 16 PnP */ | ||
160 | { .id = "CTL002a", .devs = { { "CTL0031" } } }, | ||
161 | /* Sound Blaster 16 PnP */ | ||
162 | /* Note: This card has also a CTL0051:StereoEnhance device!!! */ | ||
163 | { .id = "CTL002b", .devs = { { "CTL0031" } } }, | ||
164 | /* Sound Blaster 16 PnP */ | ||
165 | { .id = "CTL002c", .devs = { { "CTL0031" } } }, | ||
166 | /* Sound Blaster Vibra16S */ | ||
167 | { .id = "CTL0051", .devs = { { "CTL0001" } } }, | ||
168 | /* Sound Blaster Vibra16C */ | ||
169 | { .id = "CTL0070", .devs = { { "CTL0001" } } }, | ||
170 | /* Sound Blaster Vibra16CL - added by ctm@ardi.com */ | ||
171 | { .id = "CTL0080", .devs = { { "CTL0041" } } }, | ||
172 | /* Sound Blaster 16 'value' PnP. It says model ct4130 on the pcb, */ | ||
173 | /* but ct4131 on a sticker on the board.. */ | ||
174 | { .id = "CTL0086", .devs = { { "CTL0041" } } }, | ||
175 | /* Sound Blaster Vibra16X */ | ||
176 | { .id = "CTL00f0", .devs = { { "CTL0043" } } }, | ||
177 | #else /* SNDRV_SBAWE defined */ | ||
178 | /* Sound Blaster AWE 32 PnP */ | ||
179 | { .id = "CTL0035", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
180 | /* Sound Blaster AWE 32 PnP */ | ||
181 | { .id = "CTL0039", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
182 | /* Sound Blaster AWE 32 PnP */ | ||
183 | { .id = "CTL0042", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
184 | /* Sound Blaster AWE 32 PnP */ | ||
185 | { .id = "CTL0043", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
186 | /* Sound Blaster AWE 32 PnP */ | ||
187 | /* Note: This card has also a CTL0051:StereoEnhance device!!! */ | ||
188 | { .id = "CTL0044", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
189 | /* Sound Blaster AWE 32 PnP */ | ||
190 | /* Note: This card has also a CTL0051:StereoEnhance device!!! */ | ||
191 | { .id = "CTL0045", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
192 | /* Sound Blaster AWE 32 PnP */ | ||
193 | { .id = "CTL0046", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
194 | /* Sound Blaster AWE 32 PnP */ | ||
195 | { .id = "CTL0047", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
196 | /* Sound Blaster AWE 32 PnP */ | ||
197 | { .id = "CTL0048", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
198 | /* Sound Blaster AWE 32 PnP */ | ||
199 | { .id = "CTL0054", .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
200 | /* Sound Blaster AWE 32 PnP */ | ||
201 | { .id = "CTL009a", .devs = { { "CTL0041" }, { "CTL0021" } } }, | ||
202 | /* Sound Blaster AWE 32 PnP */ | ||
203 | { .id = "CTL009c", .devs = { { "CTL0041" }, { "CTL0021" } } }, | ||
204 | /* Sound Blaster 32 PnP */ | ||
205 | { .id = "CTL009f", .devs = { { "CTL0041" }, { "CTL0021" } } }, | ||
206 | /* Sound Blaster AWE 64 PnP */ | ||
207 | { .id = "CTL009d", .devs = { { "CTL0042" }, { "CTL0022" } } }, | ||
208 | /* Sound Blaster AWE 64 PnP Gold */ | ||
209 | { .id = "CTL009e", .devs = { { "CTL0044" }, { "CTL0023" } } }, | ||
210 | /* Sound Blaster AWE 64 PnP Gold */ | ||
211 | { .id = "CTL00b2", .devs = { { "CTL0044" }, { "CTL0023" } } }, | ||
212 | /* Sound Blaster AWE 64 PnP */ | ||
213 | { .id = "CTL00c1", .devs = { { "CTL0042" }, { "CTL0022" } } }, | ||
214 | /* Sound Blaster AWE 64 PnP */ | ||
215 | { .id = "CTL00c3", .devs = { { "CTL0045" }, { "CTL0022" } } }, | ||
216 | /* Sound Blaster AWE 64 PnP */ | ||
217 | { .id = "CTL00c5", .devs = { { "CTL0045" }, { "CTL0022" } } }, | ||
218 | /* Sound Blaster AWE 64 PnP */ | ||
219 | { .id = "CTL00c7", .devs = { { "CTL0045" }, { "CTL0022" } } }, | ||
220 | /* Sound Blaster AWE 64 PnP */ | ||
221 | { .id = "CTL00e4", .devs = { { "CTL0045" }, { "CTL0022" } } }, | ||
222 | /* Sound Blaster AWE 64 PnP */ | ||
223 | { .id = "CTL00e9", .devs = { { "CTL0045" }, { "CTL0022" } } }, | ||
224 | /* Sound Blaster 16 PnP (AWE) */ | ||
225 | { .id = "CTL00ed", .devs = { { "CTL0041" }, { "CTL0070" } } }, | ||
226 | /* Generic entries */ | ||
227 | { .id = "CTLXXXX" , .devs = { { "CTL0031" }, { "CTL0021" } } }, | ||
228 | { .id = "CTLXXXX" , .devs = { { "CTL0041" }, { "CTL0021" } } }, | ||
229 | { .id = "CTLXXXX" , .devs = { { "CTL0042" }, { "CTL0022" } } }, | ||
230 | { .id = "CTLXXXX" , .devs = { { "CTL0044" }, { "CTL0023" } } }, | ||
231 | { .id = "CTLXXXX" , .devs = { { "CTL0045" }, { "CTL0022" } } }, | ||
232 | #endif /* SNDRV_SBAWE */ | ||
233 | /* Sound Blaster 16 PnP (Virtual PC 2004)*/ | ||
234 | { .id = "tBA03b0", .devs = { { "PNPb003" } } }, | ||
235 | { .id = "", } | ||
236 | }; | ||
237 | |||
238 | MODULE_DEVICE_TABLE(pnp_card, snd_sb16_pnpids); | ||
239 | |||
240 | #endif /* CONFIG_PNP */ | ||
241 | |||
242 | #ifdef SNDRV_SBAWE_EMU8000 | ||
243 | #define DRIVER_NAME "snd-card-sbawe" | ||
244 | #else | ||
245 | #define DRIVER_NAME "snd-card-sb16" | ||
246 | #endif | ||
247 | |||
248 | #ifdef CONFIG_PNP | ||
249 | |||
250 | static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard, | ||
251 | struct pnp_card_link *card, | ||
252 | const struct pnp_card_device_id *id) | ||
253 | { | ||
254 | struct pnp_dev *pdev; | ||
255 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
256 | int err; | ||
257 | |||
258 | if (!cfg) | ||
259 | return -ENOMEM; | ||
260 | acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
261 | if (acard->dev == NULL) { | ||
262 | kfree(cfg); | ||
263 | return -ENODEV; | ||
264 | } | ||
265 | #ifdef SNDRV_SBAWE_EMU8000 | ||
266 | acard->devwt = pnp_request_card_device(card, id->devs[1].id, acard->dev); | ||
267 | #endif | ||
268 | /* Audio initialization */ | ||
269 | pdev = acard->dev; | ||
270 | |||
271 | pnp_init_resource_table(cfg); | ||
272 | |||
273 | /* override resources */ | ||
274 | |||
275 | if (port[dev] != SNDRV_AUTO_PORT) | ||
276 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
277 | if (mpu_port[dev] != SNDRV_AUTO_PORT) | ||
278 | pnp_resource_change(&cfg->port_resource[1], mpu_port[dev], 2); | ||
279 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
280 | pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4); | ||
281 | if (dma8[dev] != SNDRV_AUTO_DMA) | ||
282 | pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); | ||
283 | if (dma16[dev] != SNDRV_AUTO_DMA) | ||
284 | pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1); | ||
285 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
286 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
287 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
288 | snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); | ||
289 | err = pnp_activate_dev(pdev); | ||
290 | if (err < 0) { | ||
291 | snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); | ||
292 | kfree(cfg); | ||
293 | return err; | ||
294 | } | ||
295 | port[dev] = pnp_port_start(pdev, 0); | ||
296 | mpu_port[dev] = pnp_port_start(pdev, 1); | ||
297 | fm_port[dev] = pnp_port_start(pdev, 2); | ||
298 | dma8[dev] = pnp_dma(pdev, 0); | ||
299 | dma16[dev] = pnp_dma(pdev, 1); | ||
300 | irq[dev] = pnp_irq(pdev, 0); | ||
301 | snd_printdd("pnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n", | ||
302 | port[dev], mpu_port[dev], fm_port[dev]); | ||
303 | snd_printdd("pnp SB16: dma1=%i, dma2=%i, irq=%i\n", | ||
304 | dma8[dev], dma16[dev], irq[dev]); | ||
305 | #ifdef SNDRV_SBAWE_EMU8000 | ||
306 | /* WaveTable initialization */ | ||
307 | pdev = acard->devwt; | ||
308 | if (pdev != NULL) { | ||
309 | pnp_init_resource_table(cfg); | ||
310 | |||
311 | /* override resources */ | ||
312 | |||
313 | if (awe_port[dev] != SNDRV_AUTO_PORT) { | ||
314 | pnp_resource_change(&cfg->port_resource[0], awe_port[dev], 4); | ||
315 | pnp_resource_change(&cfg->port_resource[1], awe_port[dev] + 0x400, 4); | ||
316 | pnp_resource_change(&cfg->port_resource[2], awe_port[dev] + 0x800, 4); | ||
317 | } | ||
318 | if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) | ||
319 | snd_printk(KERN_ERR PFX "WaveTable the requested resources are invalid, using auto config\n"); | ||
320 | err = pnp_activate_dev(pdev); | ||
321 | if (err < 0) { | ||
322 | goto __wt_error; | ||
323 | } | ||
324 | awe_port[dev] = pnp_port_start(pdev, 0); | ||
325 | snd_printdd("pnp SB16: wavetable port=0x%lx\n", pnp_port_start(pdev, 0)); | ||
326 | } else { | ||
327 | __wt_error: | ||
328 | if (pdev) { | ||
329 | pnp_release_card_device(pdev); | ||
330 | snd_printk(KERN_ERR PFX "WaveTable pnp configure failure\n"); | ||
331 | } | ||
332 | acard->devwt = NULL; | ||
333 | awe_port[dev] = -1; | ||
334 | } | ||
335 | #endif | ||
336 | kfree(cfg); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | #endif /* CONFIG_PNP */ | ||
341 | |||
342 | static void snd_sb16_free(snd_card_t *card) | ||
343 | { | ||
344 | struct snd_card_sb16 *acard = (struct snd_card_sb16 *)card->private_data; | ||
345 | |||
346 | if (acard == NULL) | ||
347 | return; | ||
348 | if (acard->fm_res) { | ||
349 | release_resource(acard->fm_res); | ||
350 | kfree_nocheck(acard->fm_res); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | static int __init snd_sb16_probe(int dev, | ||
355 | struct pnp_card_link *pcard, | ||
356 | const struct pnp_card_device_id *pid) | ||
357 | { | ||
358 | static int possible_irqs[] = {5, 9, 10, 7, -1}; | ||
359 | static int possible_dmas8[] = {1, 3, 0, -1}; | ||
360 | static int possible_dmas16[] = {5, 6, 7, -1}; | ||
361 | int xirq, xdma8, xdma16; | ||
362 | sb_t *chip; | ||
363 | snd_card_t *card; | ||
364 | struct snd_card_sb16 *acard; | ||
365 | opl3_t *opl3; | ||
366 | snd_hwdep_t *synth = NULL; | ||
367 | #ifdef CONFIG_SND_SB16_CSP | ||
368 | snd_hwdep_t *xcsp = NULL; | ||
369 | #endif | ||
370 | unsigned long flags; | ||
371 | int err; | ||
372 | |||
373 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
374 | sizeof(struct snd_card_sb16)); | ||
375 | if (card == NULL) | ||
376 | return -ENOMEM; | ||
377 | acard = (struct snd_card_sb16 *) card->private_data; | ||
378 | card->private_free = snd_sb16_free; | ||
379 | #ifdef CONFIG_PNP | ||
380 | if (isapnp[dev]) { | ||
381 | if ((err = snd_card_sb16_pnp(dev, acard, pcard, pid))) { | ||
382 | snd_card_free(card); | ||
383 | return err; | ||
384 | } | ||
385 | snd_card_set_dev(card, &pcard->card->dev); | ||
386 | } | ||
387 | #endif | ||
388 | |||
389 | xirq = irq[dev]; | ||
390 | xdma8 = dma8[dev]; | ||
391 | xdma16 = dma16[dev]; | ||
392 | #ifdef CONFIG_PNP | ||
393 | if (!isapnp[dev]) { | ||
394 | #endif | ||
395 | if (xirq == SNDRV_AUTO_IRQ) { | ||
396 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
397 | snd_card_free(card); | ||
398 | snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); | ||
399 | return -EBUSY; | ||
400 | } | ||
401 | } | ||
402 | if (xdma8 == SNDRV_AUTO_DMA) { | ||
403 | if ((xdma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) { | ||
404 | snd_card_free(card); | ||
405 | snd_printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n"); | ||
406 | return -EBUSY; | ||
407 | } | ||
408 | } | ||
409 | if (xdma16 == SNDRV_AUTO_DMA) { | ||
410 | if ((xdma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) { | ||
411 | snd_card_free(card); | ||
412 | snd_printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n"); | ||
413 | return -EBUSY; | ||
414 | } | ||
415 | } | ||
416 | /* non-PnP FM port address is hardwired with base port address */ | ||
417 | fm_port[dev] = port[dev]; | ||
418 | /* block the 0x388 port to avoid PnP conflicts */ | ||
419 | acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); | ||
420 | #ifdef SNDRV_SBAWE_EMU8000 | ||
421 | /* non-PnP AWE port address is hardwired with base port address */ | ||
422 | awe_port[dev] = port[dev] + 0x400; | ||
423 | #endif | ||
424 | #ifdef CONFIG_PNP | ||
425 | } | ||
426 | #endif | ||
427 | |||
428 | if ((err = snd_sbdsp_create(card, | ||
429 | port[dev], | ||
430 | xirq, | ||
431 | snd_sb16dsp_interrupt, | ||
432 | xdma8, | ||
433 | xdma16, | ||
434 | SB_HW_AUTO, | ||
435 | &chip)) < 0) { | ||
436 | snd_card_free(card); | ||
437 | return err; | ||
438 | } | ||
439 | if (chip->hardware != SB_HW_16) { | ||
440 | snd_card_free(card); | ||
441 | snd_printdd("SB 16 chip was not detected at 0x%lx\n", port[dev]); | ||
442 | return -ENODEV; | ||
443 | } | ||
444 | chip->mpu_port = mpu_port[dev]; | ||
445 | #ifdef CONFIG_PNP | ||
446 | if (!isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) { | ||
447 | #else | ||
448 | if ((err = snd_sb16dsp_configure(chip)) < 0) { | ||
449 | #endif | ||
450 | snd_card_free(card); | ||
451 | return -ENXIO; | ||
452 | } | ||
453 | if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { | ||
454 | snd_card_free(card); | ||
455 | return -ENXIO; | ||
456 | } | ||
457 | |||
458 | strcpy(card->driver, | ||
459 | #ifdef SNDRV_SBAWE_EMU8000 | ||
460 | awe_port[dev] > 0 ? "SB AWE" : | ||
461 | #endif | ||
462 | "SB16"); | ||
463 | strcpy(card->shortname, chip->name); | ||
464 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma ", | ||
465 | chip->name, | ||
466 | chip->port, | ||
467 | xirq); | ||
468 | if (xdma8 >= 0) | ||
469 | sprintf(card->longname + strlen(card->longname), "%d", xdma8); | ||
470 | if (xdma16 >= 0) | ||
471 | sprintf(card->longname + strlen(card->longname), "%s%d", | ||
472 | xdma8 >= 0 ? "&" : "", xdma16); | ||
473 | |||
474 | if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { | ||
475 | if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB, | ||
476 | chip->mpu_port, 0, | ||
477 | xirq, 0, &chip->rmidi)) < 0) { | ||
478 | snd_card_free(card); | ||
479 | return -ENXIO; | ||
480 | } | ||
481 | chip->rmidi_callback = snd_mpu401_uart_interrupt; | ||
482 | } | ||
483 | |||
484 | #ifdef SNDRV_SBAWE_EMU8000 | ||
485 | if (awe_port[dev] == SNDRV_AUTO_PORT) | ||
486 | awe_port[dev] = 0; /* disable */ | ||
487 | #endif | ||
488 | |||
489 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
490 | if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2, | ||
491 | OPL3_HW_OPL3, | ||
492 | acard->fm_res != NULL || fm_port[dev] == port[dev], | ||
493 | &opl3) < 0) { | ||
494 | snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", | ||
495 | fm_port[dev], fm_port[dev] + 2); | ||
496 | } else { | ||
497 | #ifdef SNDRV_SBAWE_EMU8000 | ||
498 | int seqdev = awe_port[dev] > 0 ? 2 : 1; | ||
499 | #else | ||
500 | int seqdev = 1; | ||
501 | #endif | ||
502 | if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) { | ||
503 | snd_card_free(card); | ||
504 | return -ENXIO; | ||
505 | } | ||
506 | } | ||
507 | } | ||
508 | |||
509 | if ((err = snd_sbmixer_new(chip)) < 0) { | ||
510 | snd_card_free(card); | ||
511 | return -ENXIO; | ||
512 | } | ||
513 | |||
514 | #ifdef CONFIG_SND_SB16_CSP | ||
515 | /* CSP chip on SB16ASP/AWE32 */ | ||
516 | if ((chip->hardware == SB_HW_16) && csp[dev]) { | ||
517 | snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &xcsp); | ||
518 | if (xcsp) { | ||
519 | chip->csp = xcsp->private_data; | ||
520 | chip->hardware = SB_HW_16CSP; | ||
521 | } else { | ||
522 | snd_printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1); | ||
523 | } | ||
524 | } | ||
525 | #endif | ||
526 | #ifdef SNDRV_SBAWE_EMU8000 | ||
527 | if (awe_port[dev] > 0) { | ||
528 | if (snd_emu8000_new(card, 1, awe_port[dev], | ||
529 | seq_ports[dev], NULL) < 0) { | ||
530 | snd_printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", awe_port[dev]); | ||
531 | snd_card_free(card); | ||
532 | return -ENXIO; | ||
533 | } | ||
534 | } | ||
535 | #endif | ||
536 | |||
537 | /* setup Mic AGC */ | ||
538 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
539 | snd_sbmixer_write(chip, SB_DSP4_MIC_AGC, | ||
540 | (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) | | ||
541 | (mic_agc[dev] ? 0x00 : 0x01)); | ||
542 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
543 | |||
544 | if ((err = snd_card_register(card)) < 0) { | ||
545 | snd_card_free(card); | ||
546 | return err; | ||
547 | } | ||
548 | if (pcard) | ||
549 | pnp_set_card_drvdata(pcard, card); | ||
550 | else | ||
551 | snd_sb16_legacy[dev] = card; | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int __init snd_sb16_probe_legacy_port(unsigned long xport) | ||
556 | { | ||
557 | static int dev; | ||
558 | int res; | ||
559 | |||
560 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
561 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
562 | continue; | ||
563 | #ifdef CONFIG_PNP | ||
564 | if (isapnp[dev]) | ||
565 | continue; | ||
566 | #endif | ||
567 | port[dev] = xport; | ||
568 | res = snd_sb16_probe(dev, NULL, NULL); | ||
569 | if (res < 0) | ||
570 | port[dev] = SNDRV_AUTO_PORT; | ||
571 | return res; | ||
572 | } | ||
573 | return -ENODEV; | ||
574 | } | ||
575 | |||
576 | #ifdef CONFIG_PNP | ||
577 | |||
578 | static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *card, | ||
579 | const struct pnp_card_device_id *id) | ||
580 | { | ||
581 | static int dev; | ||
582 | int res; | ||
583 | |||
584 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
585 | if (!enable[dev] || !isapnp[dev]) | ||
586 | continue; | ||
587 | res = snd_sb16_probe(dev, card, id); | ||
588 | if (res < 0) | ||
589 | return res; | ||
590 | dev++; | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | return -ENODEV; | ||
595 | } | ||
596 | |||
597 | static void __devexit snd_sb16_pnp_remove(struct pnp_card_link * pcard) | ||
598 | { | ||
599 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
600 | |||
601 | snd_card_disconnect(card); | ||
602 | snd_card_free_in_thread(card); | ||
603 | } | ||
604 | |||
605 | static struct pnp_card_driver sb16_pnpc_driver = { | ||
606 | .flags = PNP_DRIVER_RES_DISABLE, | ||
607 | .name = "sb16", | ||
608 | .id_table = snd_sb16_pnpids, | ||
609 | .probe = snd_sb16_pnp_detect, | ||
610 | .remove = __devexit_p(snd_sb16_pnp_remove), | ||
611 | }; | ||
612 | |||
613 | #endif /* CONFIG_PNP */ | ||
614 | |||
615 | static int __init alsa_card_sb16_init(void) | ||
616 | { | ||
617 | int dev, cards = 0, i; | ||
618 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; | ||
619 | |||
620 | /* legacy non-auto cards at first */ | ||
621 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
622 | if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) | ||
623 | continue; | ||
624 | #ifdef CONFIG_PNP | ||
625 | if (isapnp[dev]) | ||
626 | continue; | ||
627 | #endif | ||
628 | if (!snd_sb16_probe(dev, NULL, NULL)) { | ||
629 | cards++; | ||
630 | continue; | ||
631 | } | ||
632 | #ifdef MODULE | ||
633 | snd_printk(KERN_ERR "Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, port[dev]); | ||
634 | #endif | ||
635 | } | ||
636 | /* legacy auto configured cards */ | ||
637 | i = snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port); | ||
638 | if (i > 0) | ||
639 | cards += i; | ||
640 | |||
641 | #ifdef CONFIG_PNP | ||
642 | /* PnP cards at last */ | ||
643 | i = pnp_register_card_driver(&sb16_pnpc_driver); | ||
644 | if (i >0) | ||
645 | cards += i; | ||
646 | #endif | ||
647 | |||
648 | if (!cards) { | ||
649 | #ifdef CONFIG_PNP | ||
650 | pnp_unregister_card_driver(&sb16_pnpc_driver); | ||
651 | #endif | ||
652 | #ifdef MODULE | ||
653 | snd_printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n"); | ||
654 | #ifdef SNDRV_SBAWE_EMU8000 | ||
655 | snd_printk(KERN_ERR "In case, if you have non-AWE card, try snd-sb16 module\n"); | ||
656 | #else | ||
657 | snd_printk(KERN_ERR "In case, if you have AWE card, try snd-sbawe module\n"); | ||
658 | #endif | ||
659 | #endif | ||
660 | return -ENODEV; | ||
661 | } | ||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static void __exit alsa_card_sb16_exit(void) | ||
666 | { | ||
667 | int dev; | ||
668 | |||
669 | #ifdef CONFIG_PNP | ||
670 | /* PnP cards first */ | ||
671 | pnp_unregister_card_driver(&sb16_pnpc_driver); | ||
672 | #endif | ||
673 | for (dev = 0; dev < SNDRV_CARDS; dev++) | ||
674 | snd_card_free(snd_sb16_legacy[dev]); | ||
675 | } | ||
676 | |||
677 | module_init(alsa_card_sb16_init) | ||
678 | module_exit(alsa_card_sb16_exit) | ||
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c new file mode 100644 index 000000000000..b62920eead3d --- /dev/null +++ b/sound/isa/sb/sb16_csp.c | |||
@@ -0,0 +1,1175 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si> | ||
3 | * Takashi Iwai <tiwai@suse.de> | ||
4 | * | ||
5 | * SB16ASP/AWE32 CSP control | ||
6 | * | ||
7 | * CSP microcode loader: | ||
8 | * alsa-tools/sb16_csp/ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <sound/driver.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/control.h> | ||
32 | #include <sound/info.h> | ||
33 | #include <sound/sb16_csp.h> | ||
34 | #include <sound/initval.h> | ||
35 | |||
36 | MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>"); | ||
37 | MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | #ifdef SNDRV_LITTLE_ENDIAN | ||
41 | #define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) | ||
42 | #else | ||
43 | #define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) | ||
44 | #endif | ||
45 | #define LE_SHORT(v) le16_to_cpu(v) | ||
46 | #define LE_INT(v) le32_to_cpu(v) | ||
47 | |||
48 | #define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F') | ||
49 | #define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ') | ||
50 | #define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T') | ||
51 | #define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c') | ||
52 | #define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e') | ||
53 | #define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't') | ||
54 | #define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n') | ||
55 | |||
56 | /* | ||
57 | * RIFF data format | ||
58 | */ | ||
59 | typedef struct riff_header { | ||
60 | __u32 name; | ||
61 | __u32 len; | ||
62 | } riff_header_t; | ||
63 | |||
64 | typedef struct desc_header { | ||
65 | riff_header_t info; | ||
66 | __u16 func_nr; | ||
67 | __u16 VOC_type; | ||
68 | __u16 flags_play_rec; | ||
69 | __u16 flags_16bit_8bit; | ||
70 | __u16 flags_stereo_mono; | ||
71 | __u16 flags_rates; | ||
72 | } desc_header_t; | ||
73 | |||
74 | /* | ||
75 | * prototypes | ||
76 | */ | ||
77 | static void snd_sb_csp_free(snd_hwdep_t *hw); | ||
78 | static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file); | ||
79 | static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg); | ||
80 | static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file); | ||
81 | |||
82 | static int csp_detect(sb_t *chip, int *version); | ||
83 | static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val); | ||
84 | static int set_register(sb_t *chip, unsigned char reg, unsigned char val); | ||
85 | static int read_register(sb_t *chip, unsigned char reg); | ||
86 | static int set_mode_register(sb_t *chip, unsigned char mode); | ||
87 | static int get_version(sb_t *chip); | ||
88 | |||
89 | static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t __user * code); | ||
90 | static int snd_sb_csp_unload(snd_sb_csp_t * p); | ||
91 | static int snd_sb_csp_load_user(snd_sb_csp_t * p, const unsigned char __user *buf, int size, int load_flags); | ||
92 | static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); | ||
93 | static int snd_sb_csp_check_version(snd_sb_csp_t * p); | ||
94 | |||
95 | static int snd_sb_csp_use(snd_sb_csp_t * p); | ||
96 | static int snd_sb_csp_unuse(snd_sb_csp_t * p); | ||
97 | static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels); | ||
98 | static int snd_sb_csp_stop(snd_sb_csp_t * p); | ||
99 | static int snd_sb_csp_pause(snd_sb_csp_t * p); | ||
100 | static int snd_sb_csp_restart(snd_sb_csp_t * p); | ||
101 | |||
102 | static int snd_sb_qsound_build(snd_sb_csp_t * p); | ||
103 | static void snd_sb_qsound_destroy(snd_sb_csp_t * p); | ||
104 | static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p); | ||
105 | |||
106 | static int init_proc_entry(snd_sb_csp_t * p, int device); | ||
107 | static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); | ||
108 | |||
109 | /* | ||
110 | * Detect CSP chip and create a new instance | ||
111 | */ | ||
112 | int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep) | ||
113 | { | ||
114 | snd_sb_csp_t *p; | ||
115 | int version, err; | ||
116 | snd_hwdep_t *hw; | ||
117 | |||
118 | if (rhwdep) | ||
119 | *rhwdep = NULL; | ||
120 | |||
121 | if (csp_detect(chip, &version)) | ||
122 | return -ENODEV; | ||
123 | |||
124 | if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) | ||
125 | return err; | ||
126 | |||
127 | if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) { | ||
128 | snd_device_free(chip->card, hw); | ||
129 | return -ENOMEM; | ||
130 | } | ||
131 | p->chip = chip; | ||
132 | p->version = version; | ||
133 | |||
134 | /* CSP operators */ | ||
135 | p->ops.csp_use = snd_sb_csp_use; | ||
136 | p->ops.csp_unuse = snd_sb_csp_unuse; | ||
137 | p->ops.csp_autoload = snd_sb_csp_autoload; | ||
138 | p->ops.csp_start = snd_sb_csp_start; | ||
139 | p->ops.csp_stop = snd_sb_csp_stop; | ||
140 | p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer; | ||
141 | |||
142 | init_MUTEX(&p->access_mutex); | ||
143 | sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f)); | ||
144 | hw->iface = SNDRV_HWDEP_IFACE_SB16CSP; | ||
145 | hw->private_data = p; | ||
146 | hw->private_free = snd_sb_csp_free; | ||
147 | |||
148 | /* operators - only write/ioctl */ | ||
149 | hw->ops.open = snd_sb_csp_open; | ||
150 | hw->ops.ioctl = snd_sb_csp_ioctl; | ||
151 | hw->ops.release = snd_sb_csp_release; | ||
152 | |||
153 | /* create a proc entry */ | ||
154 | init_proc_entry(p, device); | ||
155 | if (rhwdep) | ||
156 | *rhwdep = hw; | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * free_private for hwdep instance | ||
162 | */ | ||
163 | static void snd_sb_csp_free(snd_hwdep_t *hwdep) | ||
164 | { | ||
165 | snd_sb_csp_t *p = hwdep->private_data; | ||
166 | if (p) { | ||
167 | if (p->running & SNDRV_SB_CSP_ST_RUNNING) | ||
168 | snd_sb_csp_stop(p); | ||
169 | kfree(p); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /* ------------------------------ */ | ||
174 | |||
175 | /* | ||
176 | * open the device exclusively | ||
177 | */ | ||
178 | static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file) | ||
179 | { | ||
180 | snd_sb_csp_t *p = hw->private_data; | ||
181 | return (snd_sb_csp_use(p)); | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * ioctl for hwdep device: | ||
186 | */ | ||
187 | static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) | ||
188 | { | ||
189 | snd_sb_csp_t *p = hw->private_data; | ||
190 | snd_sb_csp_info_t info; | ||
191 | snd_sb_csp_start_t start_info; | ||
192 | int err; | ||
193 | |||
194 | snd_assert(p != NULL, return -EINVAL); | ||
195 | |||
196 | if (snd_sb_csp_check_version(p)) | ||
197 | return -ENODEV; | ||
198 | |||
199 | switch (cmd) { | ||
200 | /* get information */ | ||
201 | case SNDRV_SB_CSP_IOCTL_INFO: | ||
202 | *info.codec_name = *p->codec_name; | ||
203 | info.func_nr = p->func_nr; | ||
204 | info.acc_format = p->acc_format; | ||
205 | info.acc_channels = p->acc_channels; | ||
206 | info.acc_width = p->acc_width; | ||
207 | info.acc_rates = p->acc_rates; | ||
208 | info.csp_mode = p->mode; | ||
209 | info.run_channels = p->run_channels; | ||
210 | info.run_width = p->run_width; | ||
211 | info.version = p->version; | ||
212 | info.state = p->running; | ||
213 | if (copy_to_user((void __user *)arg, &info, sizeof(info))) | ||
214 | err = -EFAULT; | ||
215 | else | ||
216 | err = 0; | ||
217 | break; | ||
218 | |||
219 | /* load CSP microcode */ | ||
220 | case SNDRV_SB_CSP_IOCTL_LOAD_CODE: | ||
221 | err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? | ||
222 | -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t __user *) arg)); | ||
223 | break; | ||
224 | case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE: | ||
225 | err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? | ||
226 | -EBUSY : snd_sb_csp_unload(p)); | ||
227 | break; | ||
228 | |||
229 | /* change CSP running state */ | ||
230 | case SNDRV_SB_CSP_IOCTL_START: | ||
231 | if (copy_from_user(&start_info, (void __user *) arg, sizeof(start_info))) | ||
232 | err = -EFAULT; | ||
233 | else | ||
234 | err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels); | ||
235 | break; | ||
236 | case SNDRV_SB_CSP_IOCTL_STOP: | ||
237 | err = snd_sb_csp_stop(p); | ||
238 | break; | ||
239 | case SNDRV_SB_CSP_IOCTL_PAUSE: | ||
240 | err = snd_sb_csp_pause(p); | ||
241 | break; | ||
242 | case SNDRV_SB_CSP_IOCTL_RESTART: | ||
243 | err = snd_sb_csp_restart(p); | ||
244 | break; | ||
245 | default: | ||
246 | err = -ENOTTY; | ||
247 | break; | ||
248 | } | ||
249 | |||
250 | return err; | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * close the device | ||
255 | */ | ||
256 | static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file) | ||
257 | { | ||
258 | snd_sb_csp_t *p = hw->private_data; | ||
259 | return (snd_sb_csp_unuse(p)); | ||
260 | } | ||
261 | |||
262 | /* ------------------------------ */ | ||
263 | |||
264 | /* | ||
265 | * acquire device | ||
266 | */ | ||
267 | static int snd_sb_csp_use(snd_sb_csp_t * p) | ||
268 | { | ||
269 | down(&p->access_mutex); | ||
270 | if (p->used) { | ||
271 | up(&p->access_mutex); | ||
272 | return -EAGAIN; | ||
273 | } | ||
274 | p->used++; | ||
275 | up(&p->access_mutex); | ||
276 | |||
277 | return 0; | ||
278 | |||
279 | } | ||
280 | |||
281 | /* | ||
282 | * release device | ||
283 | */ | ||
284 | static int snd_sb_csp_unuse(snd_sb_csp_t * p) | ||
285 | { | ||
286 | down(&p->access_mutex); | ||
287 | p->used--; | ||
288 | up(&p->access_mutex); | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * load microcode via ioctl: | ||
295 | * code is user-space pointer | ||
296 | */ | ||
297 | static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t __user * mcode) | ||
298 | { | ||
299 | snd_sb_csp_mc_header_t info; | ||
300 | |||
301 | unsigned char __user *data_ptr; | ||
302 | unsigned char __user *data_end; | ||
303 | unsigned short func_nr = 0; | ||
304 | |||
305 | riff_header_t file_h, item_h, code_h; | ||
306 | __u32 item_type; | ||
307 | desc_header_t funcdesc_h; | ||
308 | |||
309 | unsigned long flags; | ||
310 | int err; | ||
311 | |||
312 | if (copy_from_user(&info, mcode, sizeof(info))) | ||
313 | return -EFAULT; | ||
314 | data_ptr = mcode->data; | ||
315 | |||
316 | if (copy_from_user(&file_h, data_ptr, sizeof(file_h))) | ||
317 | return -EFAULT; | ||
318 | if ((file_h.name != RIFF_HEADER) || | ||
319 | (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { | ||
320 | snd_printd("%s: Invalid RIFF header\n", __FUNCTION__); | ||
321 | return -EINVAL; | ||
322 | } | ||
323 | data_ptr += sizeof(file_h); | ||
324 | data_end = data_ptr + LE_INT(file_h.len); | ||
325 | |||
326 | if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) | ||
327 | return -EFAULT; | ||
328 | if (item_type != CSP__HEADER) { | ||
329 | snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__); | ||
330 | return -EINVAL; | ||
331 | } | ||
332 | data_ptr += sizeof (item_type); | ||
333 | |||
334 | for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) { | ||
335 | if (copy_from_user(&item_h, data_ptr, sizeof(item_h))) | ||
336 | return -EFAULT; | ||
337 | data_ptr += sizeof(item_h); | ||
338 | if (item_h.name != LIST_HEADER) | ||
339 | continue; | ||
340 | |||
341 | if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) | ||
342 | return -EFAULT; | ||
343 | switch (item_type) { | ||
344 | case FUNC_HEADER: | ||
345 | if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h))) | ||
346 | return -EFAULT; | ||
347 | func_nr = LE_SHORT(funcdesc_h.func_nr); | ||
348 | break; | ||
349 | case CODE_HEADER: | ||
350 | if (func_nr != info.func_req) | ||
351 | break; /* not required function, try next */ | ||
352 | data_ptr += sizeof(item_type); | ||
353 | |||
354 | /* destroy QSound mixer element */ | ||
355 | if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { | ||
356 | snd_sb_qsound_destroy(p); | ||
357 | } | ||
358 | /* Clear all flags */ | ||
359 | p->running = 0; | ||
360 | p->mode = 0; | ||
361 | |||
362 | /* load microcode blocks */ | ||
363 | for (;;) { | ||
364 | if (data_ptr >= data_end) | ||
365 | return -EINVAL; | ||
366 | if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) | ||
367 | return -EFAULT; | ||
368 | |||
369 | /* init microcode blocks */ | ||
370 | if (code_h.name != INIT_HEADER) | ||
371 | break; | ||
372 | data_ptr += sizeof(code_h); | ||
373 | err = snd_sb_csp_load_user(p, data_ptr, LE_INT(code_h.len), | ||
374 | SNDRV_SB_CSP_LOAD_INITBLOCK); | ||
375 | if (err) | ||
376 | return err; | ||
377 | data_ptr += LE_INT(code_h.len); | ||
378 | } | ||
379 | /* main microcode block */ | ||
380 | if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) | ||
381 | return -EFAULT; | ||
382 | |||
383 | if (code_h.name != MAIN_HEADER) { | ||
384 | snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__); | ||
385 | return -EINVAL; | ||
386 | } | ||
387 | data_ptr += sizeof(code_h); | ||
388 | err = snd_sb_csp_load_user(p, data_ptr, | ||
389 | LE_INT(code_h.len), 0); | ||
390 | if (err) | ||
391 | return err; | ||
392 | |||
393 | /* fill in codec header */ | ||
394 | strlcpy(p->codec_name, info.codec_name, sizeof(p->codec_name)); | ||
395 | p->func_nr = func_nr; | ||
396 | p->mode = LE_SHORT(funcdesc_h.flags_play_rec); | ||
397 | switch (LE_SHORT(funcdesc_h.VOC_type)) { | ||
398 | case 0x0001: /* QSound decoder */ | ||
399 | if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) { | ||
400 | if (snd_sb_qsound_build(p) == 0) | ||
401 | /* set QSound flag and clear all other mode flags */ | ||
402 | p->mode = SNDRV_SB_CSP_MODE_QSOUND; | ||
403 | } | ||
404 | p->acc_format = 0; | ||
405 | break; | ||
406 | case 0x0006: /* A Law codec */ | ||
407 | p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; | ||
408 | break; | ||
409 | case 0x0007: /* Mu Law codec */ | ||
410 | p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; | ||
411 | break; | ||
412 | case 0x0011: /* what Creative thinks is IMA ADPCM codec */ | ||
413 | case 0x0200: /* Creative ADPCM codec */ | ||
414 | p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; | ||
415 | break; | ||
416 | case 201: /* Text 2 Speech decoder */ | ||
417 | /* TODO: Text2Speech handling routines */ | ||
418 | p->acc_format = 0; | ||
419 | break; | ||
420 | case 0x0202: /* Fast Speech 8 codec */ | ||
421 | case 0x0203: /* Fast Speech 10 codec */ | ||
422 | p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL; | ||
423 | break; | ||
424 | default: /* other codecs are unsupported */ | ||
425 | p->acc_format = p->acc_width = p->acc_rates = 0; | ||
426 | p->mode = 0; | ||
427 | snd_printd("%s: Unsupported CSP codec type: 0x%04x\n", | ||
428 | __FUNCTION__, | ||
429 | LE_SHORT(funcdesc_h.VOC_type)); | ||
430 | return -EINVAL; | ||
431 | } | ||
432 | p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono); | ||
433 | p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit); | ||
434 | p->acc_rates = LE_SHORT(funcdesc_h.flags_rates); | ||
435 | |||
436 | /* Decouple CSP from IRQ and DMAREQ lines */ | ||
437 | spin_lock_irqsave(&p->chip->reg_lock, flags); | ||
438 | set_mode_register(p->chip, 0xfc); | ||
439 | set_mode_register(p->chip, 0x00); | ||
440 | spin_unlock_irqrestore(&p->chip->reg_lock, flags); | ||
441 | |||
442 | /* finished loading successfully */ | ||
443 | p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */ | ||
444 | return 0; | ||
445 | } | ||
446 | } | ||
447 | snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req); | ||
448 | return -EINVAL; | ||
449 | } | ||
450 | |||
451 | /* | ||
452 | * unload CSP microcode | ||
453 | */ | ||
454 | static int snd_sb_csp_unload(snd_sb_csp_t * p) | ||
455 | { | ||
456 | if (p->running & SNDRV_SB_CSP_ST_RUNNING) | ||
457 | return -EBUSY; | ||
458 | if (!(p->running & SNDRV_SB_CSP_ST_LOADED)) | ||
459 | return -ENXIO; | ||
460 | |||
461 | /* clear supported formats */ | ||
462 | p->acc_format = 0; | ||
463 | p->acc_channels = p->acc_width = p->acc_rates = 0; | ||
464 | /* destroy QSound mixer element */ | ||
465 | if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { | ||
466 | snd_sb_qsound_destroy(p); | ||
467 | } | ||
468 | /* clear all flags */ | ||
469 | p->running = 0; | ||
470 | p->mode = 0; | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * send command sequence to DSP | ||
476 | */ | ||
477 | static inline int command_seq(sb_t *chip, const unsigned char *seq, int size) | ||
478 | { | ||
479 | int i; | ||
480 | for (i = 0; i < size; i++) { | ||
481 | if (!snd_sbdsp_command(chip, seq[i])) | ||
482 | return -EIO; | ||
483 | } | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | /* | ||
488 | * set CSP codec parameter | ||
489 | */ | ||
490 | static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val) | ||
491 | { | ||
492 | unsigned char dsp_cmd[3]; | ||
493 | |||
494 | dsp_cmd[0] = 0x05; /* CSP set codec parameter */ | ||
495 | dsp_cmd[1] = val; /* Parameter value */ | ||
496 | dsp_cmd[2] = par; /* Parameter */ | ||
497 | command_seq(chip, dsp_cmd, 3); | ||
498 | snd_sbdsp_command(chip, 0x03); /* DSP read? */ | ||
499 | if (snd_sbdsp_get_byte(chip) != par) | ||
500 | return -EIO; | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * set CSP register | ||
506 | */ | ||
507 | static int set_register(sb_t *chip, unsigned char reg, unsigned char val) | ||
508 | { | ||
509 | unsigned char dsp_cmd[3]; | ||
510 | |||
511 | dsp_cmd[0] = 0x0e; /* CSP set register */ | ||
512 | dsp_cmd[1] = reg; /* CSP Register */ | ||
513 | dsp_cmd[2] = val; /* value */ | ||
514 | return command_seq(chip, dsp_cmd, 3); | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * read CSP register | ||
519 | * return < 0 -> error | ||
520 | */ | ||
521 | static int read_register(sb_t *chip, unsigned char reg) | ||
522 | { | ||
523 | unsigned char dsp_cmd[2]; | ||
524 | |||
525 | dsp_cmd[0] = 0x0f; /* CSP read register */ | ||
526 | dsp_cmd[1] = reg; /* CSP Register */ | ||
527 | command_seq(chip, dsp_cmd, 2); | ||
528 | return snd_sbdsp_get_byte(chip); /* Read DSP value */ | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * set CSP mode register | ||
533 | */ | ||
534 | static int set_mode_register(sb_t *chip, unsigned char mode) | ||
535 | { | ||
536 | unsigned char dsp_cmd[2]; | ||
537 | |||
538 | dsp_cmd[0] = 0x04; /* CSP set mode register */ | ||
539 | dsp_cmd[1] = mode; /* mode */ | ||
540 | return command_seq(chip, dsp_cmd, 2); | ||
541 | } | ||
542 | |||
543 | /* | ||
544 | * Detect CSP | ||
545 | * return 0 if CSP exists. | ||
546 | */ | ||
547 | static int csp_detect(sb_t *chip, int *version) | ||
548 | { | ||
549 | unsigned char csp_test1, csp_test2; | ||
550 | unsigned long flags; | ||
551 | int result = -ENODEV; | ||
552 | |||
553 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
554 | |||
555 | set_codec_parameter(chip, 0x00, 0x00); | ||
556 | set_mode_register(chip, 0xfc); /* 0xfc = ?? */ | ||
557 | |||
558 | csp_test1 = read_register(chip, 0x83); | ||
559 | set_register(chip, 0x83, ~csp_test1); | ||
560 | csp_test2 = read_register(chip, 0x83); | ||
561 | if (csp_test2 != (csp_test1 ^ 0xff)) | ||
562 | goto __fail; | ||
563 | |||
564 | set_register(chip, 0x83, csp_test1); | ||
565 | csp_test2 = read_register(chip, 0x83); | ||
566 | if (csp_test2 != csp_test1) | ||
567 | goto __fail; | ||
568 | |||
569 | set_mode_register(chip, 0x00); /* 0x00 = ? */ | ||
570 | |||
571 | *version = get_version(chip); | ||
572 | snd_sbdsp_reset(chip); /* reset DSP after getversion! */ | ||
573 | if (*version >= 0x10 && *version <= 0x1f) | ||
574 | result = 0; /* valid version id */ | ||
575 | |||
576 | __fail: | ||
577 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
578 | return result; | ||
579 | } | ||
580 | |||
581 | /* | ||
582 | * get CSP version number | ||
583 | */ | ||
584 | static int get_version(sb_t *chip) | ||
585 | { | ||
586 | unsigned char dsp_cmd[2]; | ||
587 | |||
588 | dsp_cmd[0] = 0x08; /* SB_DSP_!something! */ | ||
589 | dsp_cmd[1] = 0x03; /* get chip version id? */ | ||
590 | command_seq(chip, dsp_cmd, 2); | ||
591 | |||
592 | return (snd_sbdsp_get_byte(chip)); | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * check if the CSP version is valid | ||
597 | */ | ||
598 | static int snd_sb_csp_check_version(snd_sb_csp_t * p) | ||
599 | { | ||
600 | if (p->version < 0x10 || p->version > 0x1f) { | ||
601 | snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version); | ||
602 | return 1; | ||
603 | } | ||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * download microcode to CSP (microcode should have one "main" block). | ||
609 | */ | ||
610 | static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags) | ||
611 | { | ||
612 | int status, i; | ||
613 | int err; | ||
614 | int result = -EIO; | ||
615 | unsigned long flags; | ||
616 | |||
617 | spin_lock_irqsave(&p->chip->reg_lock, flags); | ||
618 | snd_sbdsp_command(p->chip, 0x01); /* CSP download command */ | ||
619 | if (snd_sbdsp_get_byte(p->chip)) { | ||
620 | snd_printd("%s: Download command failed\n", __FUNCTION__); | ||
621 | goto __fail; | ||
622 | } | ||
623 | /* Send CSP low byte (size - 1) */ | ||
624 | snd_sbdsp_command(p->chip, (unsigned char)(size - 1)); | ||
625 | /* Send high byte */ | ||
626 | snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8)); | ||
627 | /* send microcode sequence */ | ||
628 | /* load from kernel space */ | ||
629 | while (size--) { | ||
630 | if (!snd_sbdsp_command(p->chip, *buf++)) | ||
631 | goto __fail; | ||
632 | } | ||
633 | if (snd_sbdsp_get_byte(p->chip)) | ||
634 | goto __fail; | ||
635 | |||
636 | if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) { | ||
637 | i = 0; | ||
638 | /* some codecs (FastSpeech) take some time to initialize */ | ||
639 | while (1) { | ||
640 | snd_sbdsp_command(p->chip, 0x03); | ||
641 | status = snd_sbdsp_get_byte(p->chip); | ||
642 | if (status == 0x55 || ++i >= 10) | ||
643 | break; | ||
644 | udelay (10); | ||
645 | } | ||
646 | if (status != 0x55) { | ||
647 | snd_printd("%s: Microcode initialization failed\n", __FUNCTION__); | ||
648 | goto __fail; | ||
649 | } | ||
650 | } else { | ||
651 | /* | ||
652 | * Read mixer register SB_DSP4_DMASETUP after loading 'main' code. | ||
653 | * Start CSP chip if no 16bit DMA channel is set - some kind | ||
654 | * of autorun or perhaps a bugfix? | ||
655 | */ | ||
656 | spin_lock(&p->chip->mixer_lock); | ||
657 | status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP); | ||
658 | spin_unlock(&p->chip->mixer_lock); | ||
659 | if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) { | ||
660 | err = (set_codec_parameter(p->chip, 0xaa, 0x00) || | ||
661 | set_codec_parameter(p->chip, 0xff, 0x00)); | ||
662 | snd_sbdsp_reset(p->chip); /* really! */ | ||
663 | if (err) | ||
664 | goto __fail; | ||
665 | set_mode_register(p->chip, 0xc0); /* c0 = STOP */ | ||
666 | set_mode_register(p->chip, 0x70); /* 70 = RUN */ | ||
667 | } | ||
668 | } | ||
669 | result = 0; | ||
670 | |||
671 | __fail: | ||
672 | spin_unlock_irqrestore(&p->chip->reg_lock, flags); | ||
673 | return result; | ||
674 | } | ||
675 | |||
676 | static int snd_sb_csp_load_user(snd_sb_csp_t * p, const unsigned char __user *buf, int size, int load_flags) | ||
677 | { | ||
678 | int err = -ENOMEM; | ||
679 | unsigned char *kbuf = kmalloc(size, GFP_KERNEL); | ||
680 | if (kbuf) { | ||
681 | if (copy_from_user(kbuf, buf, size)) | ||
682 | err = -EFAULT; | ||
683 | else | ||
684 | err = snd_sb_csp_load(p, kbuf, size, load_flags); | ||
685 | kfree(kbuf); | ||
686 | } | ||
687 | return err; | ||
688 | } | ||
689 | |||
690 | #include "sb16_csp_codecs.h" | ||
691 | |||
692 | /* | ||
693 | * autoload hardware codec if necessary | ||
694 | * return 0 if CSP is loaded and ready to run (p->running != 0) | ||
695 | */ | ||
696 | static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode) | ||
697 | { | ||
698 | unsigned long flags; | ||
699 | int err = 0; | ||
700 | |||
701 | /* if CSP is running or manually loaded then exit */ | ||
702 | if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED)) | ||
703 | return -EBUSY; | ||
704 | |||
705 | /* autoload microcode only if requested hardware codec is not already loaded */ | ||
706 | if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) { | ||
707 | p->running = SNDRV_SB_CSP_ST_AUTO; | ||
708 | } else { | ||
709 | switch (pcm_sfmt) { | ||
710 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
711 | err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0); | ||
712 | p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; | ||
713 | p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; | ||
714 | break; | ||
715 | case SNDRV_PCM_FORMAT_A_LAW: | ||
716 | err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0); | ||
717 | p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; | ||
718 | p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; | ||
719 | break; | ||
720 | case SNDRV_PCM_FORMAT_IMA_ADPCM: | ||
721 | err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init), | ||
722 | SNDRV_SB_CSP_LOAD_INITBLOCK); | ||
723 | if (err) | ||
724 | break; | ||
725 | if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) { | ||
726 | err = snd_sb_csp_load(p, &ima_adpcm_playback[0], | ||
727 | sizeof(ima_adpcm_playback), 0); | ||
728 | p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE; | ||
729 | } else { | ||
730 | err = snd_sb_csp_load(p, &ima_adpcm_capture[0], | ||
731 | sizeof(ima_adpcm_capture), 0); | ||
732 | p->mode = SNDRV_SB_CSP_MODE_DSP_READ; | ||
733 | } | ||
734 | p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; | ||
735 | break; | ||
736 | default: | ||
737 | /* Decouple CSP from IRQ and DMAREQ lines */ | ||
738 | if (p->running & SNDRV_SB_CSP_ST_AUTO) { | ||
739 | spin_lock_irqsave(&p->chip->reg_lock, flags); | ||
740 | set_mode_register(p->chip, 0xfc); | ||
741 | set_mode_register(p->chip, 0x00); | ||
742 | spin_unlock_irqrestore(&p->chip->reg_lock, flags); | ||
743 | p->running = 0; /* clear autoloaded flag */ | ||
744 | } | ||
745 | return -EINVAL; | ||
746 | } | ||
747 | if (err) { | ||
748 | p->acc_format = 0; | ||
749 | p->acc_channels = p->acc_width = p->acc_rates = 0; | ||
750 | |||
751 | p->running = 0; /* clear autoloaded flag */ | ||
752 | p->mode = 0; | ||
753 | return (err); | ||
754 | } else { | ||
755 | p->running = SNDRV_SB_CSP_ST_AUTO; /* set autoloaded flag */ | ||
756 | p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT; /* only 16 bit data */ | ||
757 | p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO; | ||
758 | p->acc_rates = SNDRV_SB_CSP_RATE_ALL; /* HW codecs accept all rates */ | ||
759 | } | ||
760 | |||
761 | } | ||
762 | return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO; | ||
763 | } | ||
764 | |||
765 | /* | ||
766 | * start CSP | ||
767 | */ | ||
768 | static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels) | ||
769 | { | ||
770 | unsigned char s_type; /* sample type */ | ||
771 | unsigned char mixL, mixR; | ||
772 | int result = -EIO; | ||
773 | unsigned long flags; | ||
774 | |||
775 | if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) { | ||
776 | snd_printd("%s: Microcode not loaded\n", __FUNCTION__); | ||
777 | return -ENXIO; | ||
778 | } | ||
779 | if (p->running & SNDRV_SB_CSP_ST_RUNNING) { | ||
780 | snd_printd("%s: CSP already running\n", __FUNCTION__); | ||
781 | return -EBUSY; | ||
782 | } | ||
783 | if (!(sample_width & p->acc_width)) { | ||
784 | snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__); | ||
785 | return -EINVAL; | ||
786 | } | ||
787 | if (!(channels & p->acc_channels)) { | ||
788 | snd_printd("%s: Invalid number of channels\n", __FUNCTION__); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | |||
792 | /* Mute PCM volume */ | ||
793 | spin_lock_irqsave(&p->chip->mixer_lock, flags); | ||
794 | mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); | ||
795 | mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); | ||
796 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); | ||
797 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); | ||
798 | |||
799 | spin_lock(&p->chip->reg_lock); | ||
800 | set_mode_register(p->chip, 0xc0); /* c0 = STOP */ | ||
801 | set_mode_register(p->chip, 0x70); /* 70 = RUN */ | ||
802 | |||
803 | s_type = 0x00; | ||
804 | if (channels == SNDRV_SB_CSP_MONO) | ||
805 | s_type = 0x11; /* 000n 000n (n = 1 if mono) */ | ||
806 | if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT) | ||
807 | s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */ | ||
808 | |||
809 | if (set_codec_parameter(p->chip, 0x81, s_type)) { | ||
810 | snd_printd("%s: Set sample type command failed\n", __FUNCTION__); | ||
811 | goto __fail; | ||
812 | } | ||
813 | if (set_codec_parameter(p->chip, 0x80, 0x00)) { | ||
814 | snd_printd("%s: Codec start command failed\n", __FUNCTION__); | ||
815 | goto __fail; | ||
816 | } | ||
817 | p->run_width = sample_width; | ||
818 | p->run_channels = channels; | ||
819 | |||
820 | p->running |= SNDRV_SB_CSP_ST_RUNNING; | ||
821 | |||
822 | if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) { | ||
823 | set_codec_parameter(p->chip, 0xe0, 0x01); | ||
824 | /* enable QSound decoder */ | ||
825 | set_codec_parameter(p->chip, 0x00, 0xff); | ||
826 | set_codec_parameter(p->chip, 0x01, 0xff); | ||
827 | p->running |= SNDRV_SB_CSP_ST_QSOUND; | ||
828 | /* set QSound startup value */ | ||
829 | snd_sb_csp_qsound_transfer(p); | ||
830 | } | ||
831 | result = 0; | ||
832 | |||
833 | __fail: | ||
834 | spin_unlock(&p->chip->reg_lock); | ||
835 | |||
836 | /* restore PCM volume */ | ||
837 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); | ||
838 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); | ||
839 | spin_unlock_irqrestore(&p->chip->mixer_lock, flags); | ||
840 | |||
841 | return result; | ||
842 | } | ||
843 | |||
844 | /* | ||
845 | * stop CSP | ||
846 | */ | ||
847 | static int snd_sb_csp_stop(snd_sb_csp_t * p) | ||
848 | { | ||
849 | int result; | ||
850 | unsigned char mixL, mixR; | ||
851 | unsigned long flags; | ||
852 | |||
853 | if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) | ||
854 | return 0; | ||
855 | |||
856 | /* Mute PCM volume */ | ||
857 | spin_lock_irqsave(&p->chip->mixer_lock, flags); | ||
858 | mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); | ||
859 | mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); | ||
860 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); | ||
861 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); | ||
862 | |||
863 | spin_lock(&p->chip->reg_lock); | ||
864 | if (p->running & SNDRV_SB_CSP_ST_QSOUND) { | ||
865 | set_codec_parameter(p->chip, 0xe0, 0x01); | ||
866 | /* disable QSound decoder */ | ||
867 | set_codec_parameter(p->chip, 0x00, 0x00); | ||
868 | set_codec_parameter(p->chip, 0x01, 0x00); | ||
869 | |||
870 | p->running &= ~SNDRV_SB_CSP_ST_QSOUND; | ||
871 | } | ||
872 | result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */ | ||
873 | spin_unlock(&p->chip->reg_lock); | ||
874 | |||
875 | /* restore PCM volume */ | ||
876 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); | ||
877 | snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); | ||
878 | spin_unlock_irqrestore(&p->chip->mixer_lock, flags); | ||
879 | |||
880 | if (!(result)) | ||
881 | p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING); | ||
882 | return result; | ||
883 | } | ||
884 | |||
885 | /* | ||
886 | * pause CSP codec and hold DMA transfer | ||
887 | */ | ||
888 | static int snd_sb_csp_pause(snd_sb_csp_t * p) | ||
889 | { | ||
890 | int result; | ||
891 | unsigned long flags; | ||
892 | |||
893 | if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) | ||
894 | return -EBUSY; | ||
895 | |||
896 | spin_lock_irqsave(&p->chip->reg_lock, flags); | ||
897 | result = set_codec_parameter(p->chip, 0x80, 0xff); | ||
898 | spin_unlock_irqrestore(&p->chip->reg_lock, flags); | ||
899 | if (!(result)) | ||
900 | p->running |= SNDRV_SB_CSP_ST_PAUSED; | ||
901 | |||
902 | return result; | ||
903 | } | ||
904 | |||
905 | /* | ||
906 | * restart CSP codec and resume DMA transfer | ||
907 | */ | ||
908 | static int snd_sb_csp_restart(snd_sb_csp_t * p) | ||
909 | { | ||
910 | int result; | ||
911 | unsigned long flags; | ||
912 | |||
913 | if (!(p->running & SNDRV_SB_CSP_ST_PAUSED)) | ||
914 | return -EBUSY; | ||
915 | |||
916 | spin_lock_irqsave(&p->chip->reg_lock, flags); | ||
917 | result = set_codec_parameter(p->chip, 0x80, 0x00); | ||
918 | spin_unlock_irqrestore(&p->chip->reg_lock, flags); | ||
919 | if (!(result)) | ||
920 | p->running &= ~SNDRV_SB_CSP_ST_PAUSED; | ||
921 | |||
922 | return result; | ||
923 | } | ||
924 | |||
925 | /* ------------------------------ */ | ||
926 | |||
927 | /* | ||
928 | * QSound mixer control for PCM | ||
929 | */ | ||
930 | |||
931 | static int snd_sb_qsound_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
932 | { | ||
933 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
934 | uinfo->count = 1; | ||
935 | uinfo->value.integer.min = 0; | ||
936 | uinfo->value.integer.max = 1; | ||
937 | return 0; | ||
938 | } | ||
939 | |||
940 | static int snd_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
941 | { | ||
942 | snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); | ||
943 | |||
944 | ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0; | ||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
949 | { | ||
950 | snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); | ||
951 | unsigned long flags; | ||
952 | int change; | ||
953 | unsigned char nval; | ||
954 | |||
955 | nval = ucontrol->value.integer.value[0] & 0x01; | ||
956 | spin_lock_irqsave(&p->q_lock, flags); | ||
957 | change = p->q_enabled != nval; | ||
958 | p->q_enabled = nval; | ||
959 | spin_unlock_irqrestore(&p->q_lock, flags); | ||
960 | return change; | ||
961 | } | ||
962 | |||
963 | static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
964 | { | ||
965 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
966 | uinfo->count = 2; | ||
967 | uinfo->value.integer.min = 0; | ||
968 | uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; | ||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
973 | { | ||
974 | snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); | ||
975 | unsigned long flags; | ||
976 | |||
977 | spin_lock_irqsave(&p->q_lock, flags); | ||
978 | ucontrol->value.integer.value[0] = p->qpos_left; | ||
979 | ucontrol->value.integer.value[1] = p->qpos_right; | ||
980 | spin_unlock_irqrestore(&p->q_lock, flags); | ||
981 | return 0; | ||
982 | } | ||
983 | |||
984 | static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
985 | { | ||
986 | snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); | ||
987 | unsigned long flags; | ||
988 | int change; | ||
989 | unsigned char nval1, nval2; | ||
990 | |||
991 | nval1 = ucontrol->value.integer.value[0]; | ||
992 | if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) | ||
993 | nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; | ||
994 | nval2 = ucontrol->value.integer.value[1]; | ||
995 | if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) | ||
996 | nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; | ||
997 | spin_lock_irqsave(&p->q_lock, flags); | ||
998 | change = p->qpos_left != nval1 || p->qpos_right != nval2; | ||
999 | p->qpos_left = nval1; | ||
1000 | p->qpos_right = nval2; | ||
1001 | p->qpos_changed = change; | ||
1002 | spin_unlock_irqrestore(&p->q_lock, flags); | ||
1003 | return change; | ||
1004 | } | ||
1005 | |||
1006 | static snd_kcontrol_new_t snd_sb_qsound_switch = { | ||
1007 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1008 | .name = "3D Control - Switch", | ||
1009 | .info = snd_sb_qsound_switch_info, | ||
1010 | .get = snd_sb_qsound_switch_get, | ||
1011 | .put = snd_sb_qsound_switch_put | ||
1012 | }; | ||
1013 | |||
1014 | static snd_kcontrol_new_t snd_sb_qsound_space = { | ||
1015 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1016 | .name = "3D Control - Space", | ||
1017 | .info = snd_sb_qsound_space_info, | ||
1018 | .get = snd_sb_qsound_space_get, | ||
1019 | .put = snd_sb_qsound_space_put | ||
1020 | }; | ||
1021 | |||
1022 | static int snd_sb_qsound_build(snd_sb_csp_t * p) | ||
1023 | { | ||
1024 | snd_card_t * card; | ||
1025 | int err; | ||
1026 | |||
1027 | snd_assert(p != NULL, return -EINVAL); | ||
1028 | |||
1029 | card = p->chip->card; | ||
1030 | p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2; | ||
1031 | p->qpos_changed = 0; | ||
1032 | |||
1033 | spin_lock_init(&p->q_lock); | ||
1034 | |||
1035 | if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0) | ||
1036 | goto __error; | ||
1037 | if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0) | ||
1038 | goto __error; | ||
1039 | |||
1040 | return 0; | ||
1041 | |||
1042 | __error: | ||
1043 | snd_sb_qsound_destroy(p); | ||
1044 | return err; | ||
1045 | } | ||
1046 | |||
1047 | static void snd_sb_qsound_destroy(snd_sb_csp_t * p) | ||
1048 | { | ||
1049 | snd_card_t * card; | ||
1050 | unsigned long flags; | ||
1051 | |||
1052 | snd_assert(p != NULL, return); | ||
1053 | |||
1054 | card = p->chip->card; | ||
1055 | |||
1056 | down_write(&card->controls_rwsem); | ||
1057 | if (p->qsound_switch) | ||
1058 | snd_ctl_remove(card, p->qsound_switch); | ||
1059 | if (p->qsound_space) | ||
1060 | snd_ctl_remove(card, p->qsound_space); | ||
1061 | up_write(&card->controls_rwsem); | ||
1062 | |||
1063 | /* cancel pending transfer of QSound parameters */ | ||
1064 | spin_lock_irqsave (&p->q_lock, flags); | ||
1065 | p->qpos_changed = 0; | ||
1066 | spin_unlock_irqrestore (&p->q_lock, flags); | ||
1067 | } | ||
1068 | |||
1069 | /* | ||
1070 | * Transfer qsound parameters to CSP, | ||
1071 | * function should be called from interrupt routine | ||
1072 | */ | ||
1073 | static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p) | ||
1074 | { | ||
1075 | int err = -ENXIO; | ||
1076 | |||
1077 | spin_lock(&p->q_lock); | ||
1078 | if (p->running & SNDRV_SB_CSP_ST_QSOUND) { | ||
1079 | set_codec_parameter(p->chip, 0xe0, 0x01); | ||
1080 | /* left channel */ | ||
1081 | set_codec_parameter(p->chip, 0x00, p->qpos_left); | ||
1082 | set_codec_parameter(p->chip, 0x02, 0x00); | ||
1083 | /* right channel */ | ||
1084 | set_codec_parameter(p->chip, 0x00, p->qpos_right); | ||
1085 | set_codec_parameter(p->chip, 0x03, 0x00); | ||
1086 | err = 0; | ||
1087 | } | ||
1088 | p->qpos_changed = 0; | ||
1089 | spin_unlock(&p->q_lock); | ||
1090 | return err; | ||
1091 | } | ||
1092 | |||
1093 | /* ------------------------------ */ | ||
1094 | |||
1095 | /* | ||
1096 | * proc interface | ||
1097 | */ | ||
1098 | static int init_proc_entry(snd_sb_csp_t * p, int device) | ||
1099 | { | ||
1100 | char name[16]; | ||
1101 | snd_info_entry_t *entry; | ||
1102 | sprintf(name, "cspD%d", device); | ||
1103 | if (! snd_card_proc_new(p->chip->card, name, &entry)) | ||
1104 | snd_info_set_text_ops(entry, p, 1024, info_read); | ||
1105 | return 0; | ||
1106 | } | ||
1107 | |||
1108 | static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) | ||
1109 | { | ||
1110 | snd_sb_csp_t *p = entry->private_data; | ||
1111 | |||
1112 | snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f)); | ||
1113 | snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'), | ||
1114 | ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'), | ||
1115 | ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'), | ||
1116 | ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-')); | ||
1117 | if (p->running & SNDRV_SB_CSP_ST_LOADED) { | ||
1118 | snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr); | ||
1119 | snd_iprintf(buffer, "Sample rates: "); | ||
1120 | if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) { | ||
1121 | snd_iprintf(buffer, "All\n"); | ||
1122 | } else { | ||
1123 | snd_iprintf(buffer, "%s%s%s%s\n", | ||
1124 | ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""), | ||
1125 | ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""), | ||
1126 | ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""), | ||
1127 | ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : "")); | ||
1128 | } | ||
1129 | if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { | ||
1130 | snd_iprintf(buffer, "QSound decoder %sabled\n", | ||
1131 | p->q_enabled ? "en" : "dis"); | ||
1132 | } else { | ||
1133 | snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n", | ||
1134 | p->acc_format, | ||
1135 | ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"), | ||
1136 | ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"), | ||
1137 | ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"), | ||
1138 | ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"), | ||
1139 | ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"), | ||
1140 | ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-")); | ||
1141 | } | ||
1142 | } | ||
1143 | if (p->running & SNDRV_SB_CSP_ST_AUTO) { | ||
1144 | snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n"); | ||
1145 | } | ||
1146 | if (p->running & SNDRV_SB_CSP_ST_RUNNING) { | ||
1147 | snd_iprintf(buffer, "Processing %dbit %s PCM samples\n", | ||
1148 | ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8), | ||
1149 | ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo")); | ||
1150 | } | ||
1151 | if (p->running & SNDRV_SB_CSP_ST_QSOUND) { | ||
1152 | snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n", | ||
1153 | p->qpos_left, p->qpos_right); | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | /* */ | ||
1158 | |||
1159 | EXPORT_SYMBOL(snd_sb_csp_new); | ||
1160 | |||
1161 | /* | ||
1162 | * INIT part | ||
1163 | */ | ||
1164 | |||
1165 | static int __init alsa_sb_csp_init(void) | ||
1166 | { | ||
1167 | return 0; | ||
1168 | } | ||
1169 | |||
1170 | static void __exit alsa_sb_csp_exit(void) | ||
1171 | { | ||
1172 | } | ||
1173 | |||
1174 | module_init(alsa_sb_csp_init) | ||
1175 | module_exit(alsa_sb_csp_exit) | ||
diff --git a/sound/isa/sb/sb16_csp_codecs.h b/sound/isa/sb/sb16_csp_codecs.h new file mode 100644 index 000000000000..f0e8b0dcb572 --- /dev/null +++ b/sound/isa/sb/sb16_csp_codecs.h | |||
@@ -0,0 +1,949 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1994 Creative Technology Ltd. | ||
3 | * Microcode files for SB16 Advanced Signal Processor | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | static unsigned char mulaw_main[] = { | ||
22 | 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, | ||
23 | 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, | ||
24 | 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, | ||
25 | 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
26 | 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, | ||
27 | 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
28 | 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, | ||
29 | 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, | ||
30 | 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, | ||
31 | 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, | ||
32 | 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, | ||
33 | 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, | ||
34 | 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, | ||
35 | 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, | ||
36 | 0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, | ||
37 | 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, | ||
38 | 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, | ||
39 | 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, | ||
40 | 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, | ||
41 | 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, | ||
42 | 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, | ||
43 | 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, | ||
44 | 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, | ||
45 | 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, | ||
46 | 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, | ||
47 | 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, | ||
48 | 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, | ||
49 | 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, | ||
50 | 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, | ||
51 | 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, | ||
52 | 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, | ||
53 | 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, | ||
54 | 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, | ||
55 | 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, | ||
56 | 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, | ||
57 | 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, | ||
58 | 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, | ||
59 | 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, | ||
60 | 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, | ||
61 | 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, | ||
62 | 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, | ||
63 | 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, | ||
64 | 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, | ||
65 | 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, | ||
66 | 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, | ||
67 | 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, | ||
68 | 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, | ||
69 | 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, | ||
70 | 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, | ||
71 | 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, | ||
72 | 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, | ||
73 | 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, | ||
74 | 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, | ||
75 | 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, | ||
76 | 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, | ||
77 | 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, | ||
78 | 0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54, | ||
79 | 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, | ||
80 | 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, | ||
81 | 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, | ||
82 | 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, | ||
83 | 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, | ||
84 | 0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, | ||
85 | 0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b, | ||
86 | 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, | ||
87 | 0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b, | ||
88 | 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, | ||
89 | 0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, | ||
90 | 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, | ||
91 | 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, | ||
92 | 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, | ||
93 | 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, | ||
94 | 0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b, | ||
95 | 0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, | ||
96 | 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, | ||
97 | 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, | ||
98 | 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, | ||
99 | 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, | ||
100 | 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, | ||
101 | 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, | ||
102 | 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, | ||
103 | 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, | ||
104 | 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, | ||
105 | 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, | ||
106 | 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, | ||
107 | 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, | ||
108 | 0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c, | ||
109 | 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, | ||
110 | 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, | ||
111 | 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, | ||
112 | 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, | ||
113 | 0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80, | ||
114 | 0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39, | ||
115 | 0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80, | ||
116 | 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, | ||
117 | 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, | ||
118 | 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, | ||
119 | 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, | ||
120 | 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, | ||
121 | 0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, | ||
122 | 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, | ||
123 | 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, | ||
124 | 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, | ||
125 | 0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, | ||
126 | 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, | ||
127 | 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, | ||
128 | 0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, | ||
129 | 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, | ||
130 | 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, | ||
131 | 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
132 | 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, | ||
133 | 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, | ||
134 | 0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, | ||
135 | 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, | ||
136 | 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, | ||
137 | 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, | ||
138 | 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, | ||
139 | 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, | ||
140 | 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, | ||
141 | 0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, | ||
142 | 0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, | ||
143 | 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, | ||
144 | 0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39, | ||
145 | 0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, | ||
146 | 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0, | ||
147 | 0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, | ||
148 | 0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b, | ||
149 | 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, | ||
150 | 0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c, | ||
151 | 0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e, | ||
152 | 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, | ||
153 | 0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18, | ||
154 | 0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10, | ||
155 | 0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80, | ||
156 | 0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10, | ||
157 | 0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10, | ||
158 | 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, | ||
159 | 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, | ||
160 | 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, | ||
161 | 0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, | ||
162 | 0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54, | ||
163 | 0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44, | ||
164 | 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54, | ||
165 | 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, | ||
166 | 0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54, | ||
167 | 0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44, | ||
168 | 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54, | ||
169 | 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, | ||
170 | 0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54, | ||
171 | 0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44, | ||
172 | 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54, | ||
173 | 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, | ||
174 | 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54, | ||
175 | 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44, | ||
176 | 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54, | ||
177 | 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
178 | 0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00 | ||
179 | }; | ||
180 | |||
181 | static unsigned char alaw_main[] = { | ||
182 | 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, | ||
183 | 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, | ||
184 | 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, | ||
185 | 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
186 | 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, | ||
187 | 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
188 | 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, | ||
189 | 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, | ||
190 | 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, | ||
191 | 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, | ||
192 | 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, | ||
193 | 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, | ||
194 | 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, | ||
195 | 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, | ||
196 | 0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, | ||
197 | 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, | ||
198 | 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, | ||
199 | 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, | ||
200 | 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, | ||
201 | 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, | ||
202 | 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, | ||
203 | 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, | ||
204 | 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, | ||
205 | 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, | ||
206 | 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, | ||
207 | 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, | ||
208 | 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, | ||
209 | 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, | ||
210 | 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, | ||
211 | 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, | ||
212 | 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, | ||
213 | 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, | ||
214 | 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, | ||
215 | 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, | ||
216 | 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, | ||
217 | 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, | ||
218 | 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, | ||
219 | 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, | ||
220 | 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, | ||
221 | 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, | ||
222 | 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, | ||
223 | 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, | ||
224 | 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, | ||
225 | 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, | ||
226 | 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, | ||
227 | 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, | ||
228 | 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, | ||
229 | 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, | ||
230 | 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, | ||
231 | 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, | ||
232 | 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, | ||
233 | 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, | ||
234 | 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, | ||
235 | 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, | ||
236 | 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, | ||
237 | 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, | ||
238 | 0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54, | ||
239 | 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, | ||
240 | 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, | ||
241 | 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, | ||
242 | 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, | ||
243 | 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, | ||
244 | 0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, | ||
245 | 0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b, | ||
246 | 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, | ||
247 | 0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b, | ||
248 | 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, | ||
249 | 0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, | ||
250 | 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, | ||
251 | 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, | ||
252 | 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, | ||
253 | 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, | ||
254 | 0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b, | ||
255 | 0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80, | ||
256 | 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, | ||
257 | 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, | ||
258 | 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, | ||
259 | 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, | ||
260 | 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, | ||
261 | 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, | ||
262 | 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, | ||
263 | 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, | ||
264 | 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, | ||
265 | 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, | ||
266 | 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, | ||
267 | 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, | ||
268 | 0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c, | ||
269 | 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, | ||
270 | 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, | ||
271 | 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, | ||
272 | 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, | ||
273 | 0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49, | ||
274 | 0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39, | ||
275 | 0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49, | ||
276 | 0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39, | ||
277 | 0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80, | ||
278 | 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, | ||
279 | 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, | ||
280 | 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, | ||
281 | 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, | ||
282 | 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, | ||
283 | 0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, | ||
284 | 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, | ||
285 | 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, | ||
286 | 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, | ||
287 | 0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, | ||
288 | 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, | ||
289 | 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, | ||
290 | 0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, | ||
291 | 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, | ||
292 | 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, | ||
293 | 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
294 | 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, | ||
295 | 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, | ||
296 | 0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, | ||
297 | 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, | ||
298 | 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, | ||
299 | 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, | ||
300 | 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, | ||
301 | 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, | ||
302 | 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, | ||
303 | 0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, | ||
304 | 0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, | ||
305 | 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, | ||
306 | 0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10, | ||
307 | 0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44, | ||
308 | 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0, | ||
309 | 0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44, | ||
310 | 0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b, | ||
311 | 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, | ||
312 | 0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c, | ||
313 | 0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e, | ||
314 | 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, | ||
315 | 0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10, | ||
316 | 0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b, | ||
317 | 0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80, | ||
318 | 0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80, | ||
319 | 0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19, | ||
320 | 0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49, | ||
321 | 0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18, | ||
322 | 0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb, | ||
323 | 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80, | ||
324 | 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, | ||
325 | 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, | ||
326 | 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, | ||
327 | 0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, | ||
328 | 0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54, | ||
329 | 0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44, | ||
330 | 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54, | ||
331 | 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, | ||
332 | 0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54, | ||
333 | 0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44, | ||
334 | 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54, | ||
335 | 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, | ||
336 | 0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54, | ||
337 | 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44, | ||
338 | 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54, | ||
339 | 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, | ||
340 | 0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54, | ||
341 | 0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44, | ||
342 | 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54, | ||
343 | 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
344 | 0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00 | ||
345 | }; | ||
346 | |||
347 | |||
348 | static unsigned char ima_adpcm_init[] = { | ||
349 | 0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45, | ||
350 | 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, | ||
351 | 0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b, | ||
352 | 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
353 | 0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, | ||
354 | 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, | ||
355 | 0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e, | ||
356 | 0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
357 | 0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
358 | 0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
359 | 0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
360 | 0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
361 | 0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
362 | 0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
363 | 0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
364 | 0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
365 | 0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
366 | 0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
367 | 0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
368 | 0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
369 | 0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
370 | 0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
371 | 0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
372 | 0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
373 | 0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
374 | 0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
375 | 0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
376 | 0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
377 | 0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
378 | 0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
379 | 0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
380 | 0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
381 | 0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
382 | 0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
383 | 0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
384 | 0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
385 | 0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
386 | 0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
387 | 0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
388 | 0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
389 | 0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
390 | 0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
391 | 0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
392 | 0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
393 | 0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
394 | 0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
395 | 0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
396 | 0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
397 | 0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
398 | 0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
399 | 0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
400 | 0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
401 | 0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
402 | 0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
403 | 0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
404 | 0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
405 | 0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
406 | 0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
407 | 0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
408 | 0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
409 | 0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
410 | 0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
411 | 0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
412 | 0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
413 | 0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
414 | 0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
415 | 0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
416 | 0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
417 | 0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
418 | 0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
419 | 0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
420 | 0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
421 | 0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
422 | 0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
423 | 0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
424 | 0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
425 | 0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
426 | 0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
427 | 0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
428 | 0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
429 | 0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
430 | 0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
431 | 0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
432 | 0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
433 | 0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
434 | 0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
435 | 0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
436 | 0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
437 | 0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
438 | 0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
439 | 0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
440 | 0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
441 | 0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
442 | 0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
443 | 0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
444 | 0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
445 | 0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
446 | 0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, | ||
447 | 0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
448 | 0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
449 | 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, | ||
450 | 0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b, | ||
451 | 0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b, | ||
452 | 0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b, | ||
453 | 0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b, | ||
454 | 0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b, | ||
455 | 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, | ||
456 | 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, | ||
457 | 0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
458 | 0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
459 | 0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
460 | 0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
461 | 0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
462 | 0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
463 | 0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
464 | 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
465 | 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, | ||
466 | 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19, | ||
467 | 0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82, | ||
468 | 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, | ||
469 | 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, | ||
470 | 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, | ||
471 | 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, | ||
472 | 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, | ||
473 | 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, | ||
474 | 0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19, | ||
475 | 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, | ||
476 | 0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49, | ||
477 | 0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00 | ||
478 | }; | ||
479 | |||
480 | static unsigned char ima_adpcm_playback[] = { | ||
481 | 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, | ||
482 | 0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44, | ||
483 | 0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, | ||
484 | 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
485 | 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
486 | 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49, | ||
487 | 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, | ||
488 | 0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80, | ||
489 | 0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b, | ||
490 | 0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b, | ||
491 | 0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, | ||
492 | 0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, | ||
493 | 0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b, | ||
494 | 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, | ||
495 | 0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80, | ||
496 | 0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, | ||
497 | 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, | ||
498 | 0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14, | ||
499 | 0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8, | ||
500 | 0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49, | ||
501 | 0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39, | ||
502 | 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, | ||
503 | 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, | ||
504 | 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, | ||
505 | 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, | ||
506 | 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, | ||
507 | 0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4, | ||
508 | 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, | ||
509 | 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, | ||
510 | 0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, | ||
511 | 0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, | ||
512 | 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, | ||
513 | 0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, | ||
514 | 0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, | ||
515 | 0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, | ||
516 | 0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, | ||
517 | 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, | ||
518 | 0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, | ||
519 | 0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, | ||
520 | 0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, | ||
521 | 0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, | ||
522 | 0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, | ||
523 | 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, | ||
524 | 0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, | ||
525 | 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, | ||
526 | 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, | ||
527 | 0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, | ||
528 | 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, | ||
529 | 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, | ||
530 | 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, | ||
531 | 0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44, | ||
532 | 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, | ||
533 | 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, | ||
534 | 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, | ||
535 | 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, | ||
536 | 0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, | ||
537 | 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, | ||
538 | 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, | ||
539 | 0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, | ||
540 | 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, | ||
541 | 0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, | ||
542 | 0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, | ||
543 | 0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84, | ||
544 | 0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39, | ||
545 | 0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19, | ||
546 | 0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19, | ||
547 | 0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, | ||
548 | 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, | ||
549 | 0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19, | ||
550 | 0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, | ||
551 | 0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, | ||
552 | 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, | ||
553 | 0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54, | ||
554 | 0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44, | ||
555 | 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, | ||
556 | 0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44, | ||
557 | 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, | ||
558 | 0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84, | ||
559 | 0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, | ||
560 | 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, | ||
561 | 0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b, | ||
562 | 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, | ||
563 | 0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49, | ||
564 | 0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a, | ||
565 | 0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a, | ||
566 | 0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, | ||
567 | 0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44, | ||
568 | 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, | ||
569 | 0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19, | ||
570 | 0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b, | ||
571 | 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, | ||
572 | 0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19, | ||
573 | 0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39, | ||
574 | 0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a, | ||
575 | 0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19, | ||
576 | 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, | ||
577 | 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, | ||
578 | 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, | ||
579 | 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, | ||
580 | 0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, | ||
581 | 0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11, | ||
582 | 0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10, | ||
583 | 0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18, | ||
584 | 0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44, | ||
585 | 0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4, | ||
586 | 0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19, | ||
587 | 0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, | ||
588 | 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, | ||
589 | 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, | ||
590 | 0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, | ||
591 | 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84, | ||
592 | 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, | ||
593 | 0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, | ||
594 | 0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, | ||
595 | 0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44, | ||
596 | 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, | ||
597 | 0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19, | ||
598 | 0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44, | ||
599 | 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, | ||
600 | 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, | ||
601 | 0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a, | ||
602 | 0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39, | ||
603 | 0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39, | ||
604 | 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, | ||
605 | 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, | ||
606 | 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, | ||
607 | 0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e, | ||
608 | 0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84, | ||
609 | 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, | ||
610 | 0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44, | ||
611 | 0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44, | ||
612 | 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, | ||
613 | 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, | ||
614 | 0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, | ||
615 | 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84, | ||
616 | 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, | ||
617 | 0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b, | ||
618 | 0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80, | ||
619 | 0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80, | ||
620 | 0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e, | ||
621 | 0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf, | ||
622 | 0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b, | ||
623 | 0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, | ||
624 | 0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b, | ||
625 | 0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, | ||
626 | 0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b, | ||
627 | 0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, | ||
628 | 0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80, | ||
629 | 0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b, | ||
630 | 0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80, | ||
631 | 0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb, | ||
632 | 0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, | ||
633 | 0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19, | ||
634 | 0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a, | ||
635 | 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e, | ||
636 | 0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44, | ||
637 | 0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44, | ||
638 | 0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60, | ||
639 | 0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f, | ||
640 | 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, | ||
641 | 0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80, | ||
642 | 0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2, | ||
643 | 0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4, | ||
644 | 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, | ||
645 | 0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4, | ||
646 | 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, | ||
647 | 0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4, | ||
648 | 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, | ||
649 | 0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11, | ||
650 | 0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4, | ||
651 | 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, | ||
652 | 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22, | ||
653 | 0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80, | ||
654 | 0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b, | ||
655 | 0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a, | ||
656 | 0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02, | ||
657 | 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, | ||
658 | 0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60, | ||
659 | 0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82, | ||
660 | 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb, | ||
661 | 0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b, | ||
662 | 0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f, | ||
663 | 0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
664 | 0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f, | ||
665 | 0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a, | ||
666 | 0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, | ||
667 | 0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, | ||
668 | 0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02, | ||
669 | 0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09, | ||
670 | 0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a, | ||
671 | 0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, | ||
672 | 0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, | ||
673 | 0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
674 | 0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44, | ||
675 | 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
676 | 0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39, | ||
677 | 0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a, | ||
678 | 0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19, | ||
679 | 0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a, | ||
680 | 0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a, | ||
681 | 0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54, | ||
682 | 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44, | ||
683 | 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54, | ||
684 | 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, | ||
685 | 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54, | ||
686 | 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44, | ||
687 | 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54, | ||
688 | 0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44, | ||
689 | 0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf, | ||
690 | 0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80, | ||
691 | 0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22, | ||
692 | 0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82, | ||
693 | 0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82, | ||
694 | 0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44, | ||
695 | 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, | ||
696 | 0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, | ||
697 | 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, | ||
698 | 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, | ||
699 | 0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09, | ||
700 | 0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80, | ||
701 | 0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80, | ||
702 | 0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a, | ||
703 | 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, | ||
704 | 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a, | ||
705 | 0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, | ||
706 | 0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, | ||
707 | 0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, | ||
708 | 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18, | ||
709 | 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, | ||
710 | 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80, | ||
711 | 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, | ||
712 | 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, | ||
713 | 0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80, | ||
714 | 0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00 | ||
715 | }; | ||
716 | |||
717 | static unsigned char ima_adpcm_capture[] = { | ||
718 | 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, | ||
719 | 0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44, | ||
720 | 0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, | ||
721 | 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, | ||
722 | 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, | ||
723 | 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49, | ||
724 | 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, | ||
725 | 0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b, | ||
726 | 0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19, | ||
727 | 0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b, | ||
728 | 0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b, | ||
729 | 0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19, | ||
730 | 0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19, | ||
731 | 0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19, | ||
732 | 0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, | ||
733 | 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, | ||
734 | 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, | ||
735 | 0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14, | ||
736 | 0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8, | ||
737 | 0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24, | ||
738 | 0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44, | ||
739 | 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, | ||
740 | 0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18, | ||
741 | 0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19, | ||
742 | 0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8, | ||
743 | 0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8, | ||
744 | 0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, | ||
745 | 0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a, | ||
746 | 0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a, | ||
747 | 0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, | ||
748 | 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, | ||
749 | 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, | ||
750 | 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, | ||
751 | 0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4, | ||
752 | 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, | ||
753 | 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, | ||
754 | 0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, | ||
755 | 0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, | ||
756 | 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, | ||
757 | 0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, | ||
758 | 0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, | ||
759 | 0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, | ||
760 | 0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, | ||
761 | 0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, | ||
762 | 0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, | ||
763 | 0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, | ||
764 | 0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, | ||
765 | 0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, | ||
766 | 0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, | ||
767 | 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, | ||
768 | 0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, | ||
769 | 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, | ||
770 | 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, | ||
771 | 0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, | ||
772 | 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, | ||
773 | 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, | ||
774 | 0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, | ||
775 | 0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44, | ||
776 | 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, | ||
777 | 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, | ||
778 | 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, | ||
779 | 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, | ||
780 | 0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, | ||
781 | 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, | ||
782 | 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, | ||
783 | 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, | ||
784 | 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, | ||
785 | 0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, | ||
786 | 0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, | ||
787 | 0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84, | ||
788 | 0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19, | ||
789 | 0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, | ||
790 | 0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19, | ||
791 | 0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, | ||
792 | 0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, | ||
793 | 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, | ||
794 | 0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19, | ||
795 | 0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, | ||
796 | 0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, | ||
797 | 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, | ||
798 | 0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14, | ||
799 | 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80, | ||
800 | 0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49, | ||
801 | 0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80, | ||
802 | 0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b, | ||
803 | 0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b, | ||
804 | 0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80, | ||
805 | 0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44, | ||
806 | 0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, | ||
807 | 0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19, | ||
808 | 0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44, | ||
809 | 0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, | ||
810 | 0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4, | ||
811 | 0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44, | ||
812 | 0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e, | ||
813 | 0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84, | ||
814 | 0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10, | ||
815 | 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, | ||
816 | 0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b, | ||
817 | 0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10, | ||
818 | 0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80, | ||
819 | 0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a, | ||
820 | 0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4, | ||
821 | 0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4, | ||
822 | 0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4, | ||
823 | 0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44, | ||
824 | 0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18, | ||
825 | 0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, | ||
826 | 0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19, | ||
827 | 0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19, | ||
828 | 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, | ||
829 | 0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02, | ||
830 | 0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, | ||
831 | 0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f, | ||
832 | 0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44, | ||
833 | 0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, | ||
834 | 0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44, | ||
835 | 0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f, | ||
836 | 0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b, | ||
837 | 0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b, | ||
838 | 0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80, | ||
839 | 0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84, | ||
840 | 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, | ||
841 | 0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b, | ||
842 | 0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a, | ||
843 | 0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, | ||
844 | 0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a, | ||
845 | 0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44, | ||
846 | 0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18, | ||
847 | 0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb, | ||
848 | 0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a, | ||
849 | 0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8, | ||
850 | 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, | ||
851 | 0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, | ||
852 | 0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44, | ||
853 | 0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80, | ||
854 | 0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4, | ||
855 | 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, | ||
856 | 0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, | ||
857 | 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, | ||
858 | 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, | ||
859 | 0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02, | ||
860 | 0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09, | ||
861 | 0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44, | ||
862 | 0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f, | ||
863 | 0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88, | ||
864 | 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82, | ||
865 | 0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49, | ||
866 | 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, | ||
867 | 0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, | ||
868 | 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, | ||
869 | 0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f, | ||
870 | 0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e, | ||
871 | 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, | ||
872 | 0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80, | ||
873 | 0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14, | ||
874 | 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, | ||
875 | 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, | ||
876 | 0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54, | ||
877 | 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, | ||
878 | 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, | ||
879 | 0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54, | ||
880 | 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, | ||
881 | 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, | ||
882 | 0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54, | ||
883 | 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, | ||
884 | 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, | ||
885 | 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, | ||
886 | 0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4, | ||
887 | 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, | ||
888 | 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, | ||
889 | 0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, | ||
890 | 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e, | ||
891 | 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, | ||
892 | 0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, | ||
893 | 0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, | ||
894 | 0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, | ||
895 | 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, | ||
896 | 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, | ||
897 | 0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60, | ||
898 | 0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46, | ||
899 | 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, | ||
900 | 0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0, | ||
901 | 0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14, | ||
902 | 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, | ||
903 | 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, | ||
904 | 0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54, | ||
905 | 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, | ||
906 | 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, | ||
907 | 0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54, | ||
908 | 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, | ||
909 | 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, | ||
910 | 0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54, | ||
911 | 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, | ||
912 | 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, | ||
913 | 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, | ||
914 | 0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4, | ||
915 | 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, | ||
916 | 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, | ||
917 | 0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, | ||
918 | 0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e, | ||
919 | 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, | ||
920 | 0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, | ||
921 | 0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, | ||
922 | 0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, | ||
923 | 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, | ||
924 | 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, | ||
925 | 0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46, | ||
926 | 0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, | ||
927 | 0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80, | ||
928 | 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, | ||
929 | 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46, | ||
930 | 0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14, | ||
931 | 0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a, | ||
932 | 0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19, | ||
933 | 0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a, | ||
934 | 0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19, | ||
935 | 0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a, | ||
936 | 0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19, | ||
937 | 0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a, | ||
938 | 0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19, | ||
939 | 0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a, | ||
940 | 0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19, | ||
941 | 0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a, | ||
942 | 0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19, | ||
943 | 0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a, | ||
944 | 0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19, | ||
945 | 0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a, | ||
946 | 0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19, | ||
947 | 0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b, | ||
948 | 0xa6, 0xc5, 0x11, 0x00 | ||
949 | }; | ||
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c new file mode 100644 index 000000000000..a6a0fa516268 --- /dev/null +++ b/sound/isa/sb/sb16_main.c | |||
@@ -0,0 +1,916 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of 16-bit SoundBlaster cards and clones | ||
4 | * Note: This is very ugly hardware which uses one 8-bit DMA channel and | ||
5 | * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't | ||
6 | * transfer 16-bit samples and 16-bit DMA channels can't transfer | ||
7 | * 8-bit samples. This make full duplex more complicated than | ||
8 | * can be... People, don't buy these soundcards for full 16-bit | ||
9 | * duplex!!! | ||
10 | * Note: 16-bit wide is assigned to first direction which made request. | ||
11 | * With full duplex - playback is preferred with abstract layer. | ||
12 | * | ||
13 | * Note: Some chip revisions have hardware bug. Changing capture | ||
14 | * channel from full-duplex 8bit DMA to 16bit DMA will block | ||
15 | * 16bit DMA transfers from DSP chip (capture) until 8bit transfer | ||
16 | * to DSP chip (playback) starts. This bug can be avoided with | ||
17 | * "16bit DMA Allocation" setting set to Playback or Capture. | ||
18 | * | ||
19 | * | ||
20 | * This program is free software; you can redistribute it and/or modify | ||
21 | * it under the terms of the GNU General Public License as published by | ||
22 | * the Free Software Foundation; either version 2 of the License, or | ||
23 | * (at your option) any later version. | ||
24 | * | ||
25 | * This program is distributed in the hope that it will be useful, | ||
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
28 | * GNU General Public License for more details. | ||
29 | * | ||
30 | * You should have received a copy of the GNU General Public License | ||
31 | * along with this program; if not, write to the Free Software | ||
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | #include <sound/driver.h> | ||
37 | #include <asm/io.h> | ||
38 | #include <asm/dma.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/time.h> | ||
41 | #include <sound/core.h> | ||
42 | #include <sound/sb.h> | ||
43 | #include <sound/sb16_csp.h> | ||
44 | #include <sound/mpu401.h> | ||
45 | #include <sound/control.h> | ||
46 | #include <sound/info.h> | ||
47 | |||
48 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
49 | MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones"); | ||
50 | MODULE_LICENSE("GPL"); | ||
51 | |||
52 | #ifdef CONFIG_SND_SB16_CSP | ||
53 | static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) | ||
54 | { | ||
55 | if (chip->hardware == SB_HW_16CSP) { | ||
56 | snd_sb_csp_t *csp = chip->csp; | ||
57 | |||
58 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | ||
59 | /* manually loaded codec */ | ||
60 | if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && | ||
61 | ((1U << runtime->format) == csp->acc_format)) { | ||
62 | /* Supported runtime PCM format for playback */ | ||
63 | if (csp->ops.csp_use(csp) == 0) { | ||
64 | /* If CSP was successfully acquired */ | ||
65 | goto __start_CSP; | ||
66 | } | ||
67 | } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { | ||
68 | /* QSound decoder is loaded and enabled */ | ||
69 | if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | | ||
70 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { | ||
71 | /* Only for simple PCM formats */ | ||
72 | if (csp->ops.csp_use(csp) == 0) { | ||
73 | /* If CSP was successfully acquired */ | ||
74 | goto __start_CSP; | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | } else if (csp->ops.csp_use(csp) == 0) { | ||
79 | /* Acquire CSP and try to autoload hardware codec */ | ||
80 | if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { | ||
81 | /* Unsupported format, release CSP */ | ||
82 | csp->ops.csp_unuse(csp); | ||
83 | } else { | ||
84 | __start_CSP: | ||
85 | /* Try to start CSP */ | ||
86 | if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? | ||
87 | SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, | ||
88 | (runtime->channels > 1) ? | ||
89 | SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { | ||
90 | /* Failed, release CSP */ | ||
91 | csp->ops.csp_unuse(csp); | ||
92 | } else { | ||
93 | /* Success, CSP acquired and running */ | ||
94 | chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
101 | static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) | ||
102 | { | ||
103 | if (chip->hardware == SB_HW_16CSP) { | ||
104 | snd_sb_csp_t *csp = chip->csp; | ||
105 | |||
106 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | ||
107 | /* manually loaded codec */ | ||
108 | if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && | ||
109 | ((1U << runtime->format) == csp->acc_format)) { | ||
110 | /* Supported runtime PCM format for capture */ | ||
111 | if (csp->ops.csp_use(csp) == 0) { | ||
112 | /* If CSP was successfully acquired */ | ||
113 | goto __start_CSP; | ||
114 | } | ||
115 | } | ||
116 | } else if (csp->ops.csp_use(csp) == 0) { | ||
117 | /* Acquire CSP and try to autoload hardware codec */ | ||
118 | if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { | ||
119 | /* Unsupported format, release CSP */ | ||
120 | csp->ops.csp_unuse(csp); | ||
121 | } else { | ||
122 | __start_CSP: | ||
123 | /* Try to start CSP */ | ||
124 | if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? | ||
125 | SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, | ||
126 | (runtime->channels > 1) ? | ||
127 | SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { | ||
128 | /* Failed, release CSP */ | ||
129 | csp->ops.csp_unuse(csp); | ||
130 | } else { | ||
131 | /* Success, CSP acquired and running */ | ||
132 | chip->open = SNDRV_SB_CSP_MODE_DSP_READ; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | static void snd_sb16_csp_update(sb_t *chip) | ||
140 | { | ||
141 | if (chip->hardware == SB_HW_16CSP) { | ||
142 | snd_sb_csp_t *csp = chip->csp; | ||
143 | |||
144 | if (csp->qpos_changed) { | ||
145 | spin_lock(&chip->reg_lock); | ||
146 | csp->ops.csp_qsound_transfer (csp); | ||
147 | spin_unlock(&chip->reg_lock); | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime) | ||
153 | { | ||
154 | /* CSP decoders (QSound excluded) support only 16bit transfers */ | ||
155 | if (chip->hardware == SB_HW_16CSP) { | ||
156 | snd_sb_csp_t *csp = chip->csp; | ||
157 | |||
158 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | ||
159 | /* manually loaded codec */ | ||
160 | if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { | ||
161 | runtime->hw.formats |= csp->acc_format; | ||
162 | } | ||
163 | } else { | ||
164 | /* autoloaded codecs */ | ||
165 | runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
166 | SNDRV_PCM_FMTBIT_IMA_ADPCM; | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static void snd_sb16_csp_playback_close(sb_t *chip) | ||
172 | { | ||
173 | if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { | ||
174 | snd_sb_csp_t *csp = chip->csp; | ||
175 | |||
176 | if (csp->ops.csp_stop(csp) == 0) { | ||
177 | csp->ops.csp_unuse(csp); | ||
178 | chip->open = 0; | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime) | ||
184 | { | ||
185 | /* CSP coders support only 16bit transfers */ | ||
186 | if (chip->hardware == SB_HW_16CSP) { | ||
187 | snd_sb_csp_t *csp = chip->csp; | ||
188 | |||
189 | if (csp->running & SNDRV_SB_CSP_ST_LOADED) { | ||
190 | /* manually loaded codec */ | ||
191 | if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { | ||
192 | runtime->hw.formats |= csp->acc_format; | ||
193 | } | ||
194 | } else { | ||
195 | /* autoloaded codecs */ | ||
196 | runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
197 | SNDRV_PCM_FMTBIT_IMA_ADPCM; | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | static void snd_sb16_csp_capture_close(sb_t *chip) | ||
203 | { | ||
204 | if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { | ||
205 | snd_sb_csp_t *csp = chip->csp; | ||
206 | |||
207 | if (csp->ops.csp_stop(csp) == 0) { | ||
208 | csp->ops.csp_unuse(csp); | ||
209 | chip->open = 0; | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | #else | ||
214 | #define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ | ||
215 | #define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ | ||
216 | #define snd_sb16_csp_update(chip) /*nop*/ | ||
217 | #define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ | ||
218 | #define snd_sb16_csp_playback_close(chip) /*nop*/ | ||
219 | #define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ | ||
220 | #define snd_sb16_csp_capture_close(chip) /*nop*/ | ||
221 | #endif | ||
222 | |||
223 | |||
224 | static void snd_sb16_setup_rate(sb_t *chip, | ||
225 | unsigned short rate, | ||
226 | int channel) | ||
227 | { | ||
228 | unsigned long flags; | ||
229 | |||
230 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
231 | if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) | ||
232 | snd_sb_ack_16bit(chip); | ||
233 | else | ||
234 | snd_sb_ack_8bit(chip); | ||
235 | if (!(chip->mode & SB_RATE_LOCK)) { | ||
236 | chip->locked_rate = rate; | ||
237 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); | ||
238 | snd_sbdsp_command(chip, rate >> 8); | ||
239 | snd_sbdsp_command(chip, rate & 0xff); | ||
240 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); | ||
241 | snd_sbdsp_command(chip, rate >> 8); | ||
242 | snd_sbdsp_command(chip, rate & 0xff); | ||
243 | } | ||
244 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
245 | } | ||
246 | |||
247 | static int snd_sb16_hw_params(snd_pcm_substream_t * substream, | ||
248 | snd_pcm_hw_params_t * hw_params) | ||
249 | { | ||
250 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
251 | } | ||
252 | |||
253 | static int snd_sb16_hw_free(snd_pcm_substream_t * substream) | ||
254 | { | ||
255 | snd_pcm_lib_free_pages(substream); | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream) | ||
260 | { | ||
261 | unsigned long flags; | ||
262 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
263 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
264 | unsigned char format; | ||
265 | unsigned int size, count, dma; | ||
266 | |||
267 | snd_sb16_csp_playback_prepare(chip, runtime); | ||
268 | if (snd_pcm_format_unsigned(runtime->format) > 0) { | ||
269 | format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; | ||
270 | } else { | ||
271 | format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; | ||
272 | } | ||
273 | |||
274 | snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); | ||
275 | size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
276 | dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; | ||
277 | snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
278 | |||
279 | count = snd_pcm_lib_period_bytes(substream); | ||
280 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
281 | if (chip->mode & SB_MODE_PLAYBACK_16) { | ||
282 | count >>= 1; | ||
283 | count--; | ||
284 | snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); | ||
285 | snd_sbdsp_command(chip, format); | ||
286 | snd_sbdsp_command(chip, count & 0xff); | ||
287 | snd_sbdsp_command(chip, count >> 8); | ||
288 | snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); | ||
289 | } else { | ||
290 | count--; | ||
291 | snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); | ||
292 | snd_sbdsp_command(chip, format); | ||
293 | snd_sbdsp_command(chip, count & 0xff); | ||
294 | snd_sbdsp_command(chip, count >> 8); | ||
295 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
296 | } | ||
297 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream, | ||
302 | int cmd) | ||
303 | { | ||
304 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
305 | int result = 0; | ||
306 | |||
307 | spin_lock(&chip->reg_lock); | ||
308 | switch (cmd) { | ||
309 | case SNDRV_PCM_TRIGGER_START: | ||
310 | chip->mode |= SB_RATE_LOCK_PLAYBACK; | ||
311 | snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | ||
312 | break; | ||
313 | case SNDRV_PCM_TRIGGER_STOP: | ||
314 | snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); | ||
315 | /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ | ||
316 | if (chip->mode & SB_RATE_LOCK_CAPTURE) | ||
317 | snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | ||
318 | chip->mode &= ~SB_RATE_LOCK_PLAYBACK; | ||
319 | break; | ||
320 | default: | ||
321 | result = -EINVAL; | ||
322 | } | ||
323 | spin_unlock(&chip->reg_lock); | ||
324 | return result; | ||
325 | } | ||
326 | |||
327 | static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream) | ||
328 | { | ||
329 | unsigned long flags; | ||
330 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
331 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
332 | unsigned char format; | ||
333 | unsigned int size, count, dma; | ||
334 | |||
335 | snd_sb16_csp_capture_prepare(chip, runtime); | ||
336 | if (snd_pcm_format_unsigned(runtime->format) > 0) { | ||
337 | format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; | ||
338 | } else { | ||
339 | format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; | ||
340 | } | ||
341 | snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); | ||
342 | size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
343 | dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; | ||
344 | snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); | ||
345 | |||
346 | count = snd_pcm_lib_period_bytes(substream); | ||
347 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
348 | if (chip->mode & SB_MODE_CAPTURE_16) { | ||
349 | count >>= 1; | ||
350 | count--; | ||
351 | snd_sbdsp_command(chip, SB_DSP4_IN16_AI); | ||
352 | snd_sbdsp_command(chip, format); | ||
353 | snd_sbdsp_command(chip, count & 0xff); | ||
354 | snd_sbdsp_command(chip, count >> 8); | ||
355 | snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); | ||
356 | } else { | ||
357 | count--; | ||
358 | snd_sbdsp_command(chip, SB_DSP4_IN8_AI); | ||
359 | snd_sbdsp_command(chip, format); | ||
360 | snd_sbdsp_command(chip, count & 0xff); | ||
361 | snd_sbdsp_command(chip, count >> 8); | ||
362 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
363 | } | ||
364 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream, | ||
369 | int cmd) | ||
370 | { | ||
371 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
372 | int result = 0; | ||
373 | |||
374 | spin_lock(&chip->reg_lock); | ||
375 | switch (cmd) { | ||
376 | case SNDRV_PCM_TRIGGER_START: | ||
377 | chip->mode |= SB_RATE_LOCK_CAPTURE; | ||
378 | snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | ||
379 | break; | ||
380 | case SNDRV_PCM_TRIGGER_STOP: | ||
381 | snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); | ||
382 | /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ | ||
383 | if (chip->mode & SB_RATE_LOCK_PLAYBACK) | ||
384 | snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); | ||
385 | chip->mode &= ~SB_RATE_LOCK_CAPTURE; | ||
386 | break; | ||
387 | default: | ||
388 | result = -EINVAL; | ||
389 | } | ||
390 | spin_unlock(&chip->reg_lock); | ||
391 | return result; | ||
392 | } | ||
393 | |||
394 | irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
395 | { | ||
396 | sb_t *chip = dev_id; | ||
397 | unsigned char status; | ||
398 | int ok; | ||
399 | |||
400 | spin_lock(&chip->mixer_lock); | ||
401 | status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); | ||
402 | spin_unlock(&chip->mixer_lock); | ||
403 | if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback) | ||
404 | chip->rmidi_callback(irq, chip->rmidi->private_data, regs); | ||
405 | if (status & SB_IRQTYPE_8BIT) { | ||
406 | ok = 0; | ||
407 | if (chip->mode & SB_MODE_PLAYBACK_8) { | ||
408 | snd_pcm_period_elapsed(chip->playback_substream); | ||
409 | snd_sb16_csp_update(chip); | ||
410 | ok++; | ||
411 | } | ||
412 | if (chip->mode & SB_MODE_CAPTURE_8) { | ||
413 | snd_pcm_period_elapsed(chip->capture_substream); | ||
414 | ok++; | ||
415 | } | ||
416 | spin_lock(&chip->reg_lock); | ||
417 | if (!ok) | ||
418 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
419 | snd_sb_ack_8bit(chip); | ||
420 | spin_unlock(&chip->reg_lock); | ||
421 | } | ||
422 | if (status & SB_IRQTYPE_16BIT) { | ||
423 | ok = 0; | ||
424 | if (chip->mode & SB_MODE_PLAYBACK_16) { | ||
425 | snd_pcm_period_elapsed(chip->playback_substream); | ||
426 | snd_sb16_csp_update(chip); | ||
427 | ok++; | ||
428 | } | ||
429 | if (chip->mode & SB_MODE_CAPTURE_16) { | ||
430 | snd_pcm_period_elapsed(chip->capture_substream); | ||
431 | ok++; | ||
432 | } | ||
433 | spin_lock(&chip->reg_lock); | ||
434 | if (!ok) | ||
435 | snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); | ||
436 | snd_sb_ack_16bit(chip); | ||
437 | spin_unlock(&chip->reg_lock); | ||
438 | } | ||
439 | return IRQ_HANDLED; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | |||
444 | */ | ||
445 | |||
446 | static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream) | ||
447 | { | ||
448 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
449 | unsigned int dma; | ||
450 | size_t ptr; | ||
451 | |||
452 | dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; | ||
453 | ptr = snd_dma_pointer(dma, chip->p_dma_size); | ||
454 | return bytes_to_frames(substream->runtime, ptr); | ||
455 | } | ||
456 | |||
457 | static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream) | ||
458 | { | ||
459 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
460 | unsigned int dma; | ||
461 | size_t ptr; | ||
462 | |||
463 | dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; | ||
464 | ptr = snd_dma_pointer(dma, chip->c_dma_size); | ||
465 | return bytes_to_frames(substream->runtime, ptr); | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | |||
470 | */ | ||
471 | |||
472 | static snd_pcm_hardware_t snd_sb16_playback = | ||
473 | { | ||
474 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
475 | SNDRV_PCM_INFO_MMAP_VALID), | ||
476 | .formats = 0, | ||
477 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, | ||
478 | .rate_min = 4000, | ||
479 | .rate_max = 44100, | ||
480 | .channels_min = 1, | ||
481 | .channels_max = 2, | ||
482 | .buffer_bytes_max = (128*1024), | ||
483 | .period_bytes_min = 64, | ||
484 | .period_bytes_max = (128*1024), | ||
485 | .periods_min = 1, | ||
486 | .periods_max = 1024, | ||
487 | .fifo_size = 0, | ||
488 | }; | ||
489 | |||
490 | static snd_pcm_hardware_t snd_sb16_capture = | ||
491 | { | ||
492 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
493 | SNDRV_PCM_INFO_MMAP_VALID), | ||
494 | .formats = 0, | ||
495 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, | ||
496 | .rate_min = 4000, | ||
497 | .rate_max = 44100, | ||
498 | .channels_min = 1, | ||
499 | .channels_max = 2, | ||
500 | .buffer_bytes_max = (128*1024), | ||
501 | .period_bytes_min = 64, | ||
502 | .period_bytes_max = (128*1024), | ||
503 | .periods_min = 1, | ||
504 | .periods_max = 1024, | ||
505 | .fifo_size = 0, | ||
506 | }; | ||
507 | |||
508 | /* | ||
509 | * open/close | ||
510 | */ | ||
511 | |||
512 | static int snd_sb16_playback_open(snd_pcm_substream_t * substream) | ||
513 | { | ||
514 | unsigned long flags; | ||
515 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
516 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
517 | |||
518 | spin_lock_irqsave(&chip->open_lock, flags); | ||
519 | if (chip->mode & SB_MODE_PLAYBACK) { | ||
520 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
521 | return -EAGAIN; | ||
522 | } | ||
523 | runtime->hw = snd_sb16_playback; | ||
524 | |||
525 | /* skip if 16 bit DMA was reserved for capture */ | ||
526 | if (chip->force_mode16 & SB_MODE_CAPTURE_16) | ||
527 | goto __skip_16bit; | ||
528 | |||
529 | if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) { | ||
530 | chip->mode |= SB_MODE_PLAYBACK_16; | ||
531 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | ||
532 | /* Vibra16X hack */ | ||
533 | if (chip->dma16 <= 3) { | ||
534 | runtime->hw.buffer_bytes_max = | ||
535 | runtime->hw.period_bytes_max = 64 * 1024; | ||
536 | } else { | ||
537 | snd_sb16_csp_playback_open(chip, runtime); | ||
538 | } | ||
539 | goto __open_ok; | ||
540 | } | ||
541 | |||
542 | __skip_16bit: | ||
543 | if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) { | ||
544 | chip->mode |= SB_MODE_PLAYBACK_8; | ||
545 | /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ | ||
546 | if (chip->dma16 < 0) { | ||
547 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | ||
548 | chip->mode |= SB_MODE_PLAYBACK_16; | ||
549 | } else { | ||
550 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; | ||
551 | } | ||
552 | runtime->hw.buffer_bytes_max = | ||
553 | runtime->hw.period_bytes_max = 64 * 1024; | ||
554 | goto __open_ok; | ||
555 | } | ||
556 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
557 | return -EAGAIN; | ||
558 | |||
559 | __open_ok: | ||
560 | if (chip->hardware == SB_HW_ALS100) | ||
561 | runtime->hw.rate_max = 48000; | ||
562 | if (chip->mode & SB_RATE_LOCK) | ||
563 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | ||
564 | chip->playback_substream = substream; | ||
565 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static int snd_sb16_playback_close(snd_pcm_substream_t * substream) | ||
570 | { | ||
571 | unsigned long flags; | ||
572 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
573 | |||
574 | snd_sb16_csp_playback_close(chip); | ||
575 | spin_lock_irqsave(&chip->open_lock, flags); | ||
576 | chip->playback_substream = NULL; | ||
577 | chip->mode &= ~SB_MODE_PLAYBACK; | ||
578 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | static int snd_sb16_capture_open(snd_pcm_substream_t * substream) | ||
583 | { | ||
584 | unsigned long flags; | ||
585 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
586 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
587 | |||
588 | spin_lock_irqsave(&chip->open_lock, flags); | ||
589 | if (chip->mode & SB_MODE_CAPTURE) { | ||
590 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
591 | return -EAGAIN; | ||
592 | } | ||
593 | runtime->hw = snd_sb16_capture; | ||
594 | |||
595 | /* skip if 16 bit DMA was reserved for playback */ | ||
596 | if (chip->force_mode16 & SB_MODE_PLAYBACK_16) | ||
597 | goto __skip_16bit; | ||
598 | |||
599 | if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { | ||
600 | chip->mode |= SB_MODE_CAPTURE_16; | ||
601 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | ||
602 | /* Vibra16X hack */ | ||
603 | if (chip->dma16 <= 3) { | ||
604 | runtime->hw.buffer_bytes_max = | ||
605 | runtime->hw.period_bytes_max = 64 * 1024; | ||
606 | } else { | ||
607 | snd_sb16_csp_capture_open(chip, runtime); | ||
608 | } | ||
609 | goto __open_ok; | ||
610 | } | ||
611 | |||
612 | __skip_16bit: | ||
613 | if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { | ||
614 | chip->mode |= SB_MODE_CAPTURE_8; | ||
615 | /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ | ||
616 | if (chip->dma16 < 0) { | ||
617 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; | ||
618 | chip->mode |= SB_MODE_CAPTURE_16; | ||
619 | } else { | ||
620 | runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; | ||
621 | } | ||
622 | runtime->hw.buffer_bytes_max = | ||
623 | runtime->hw.period_bytes_max = 64 * 1024; | ||
624 | goto __open_ok; | ||
625 | } | ||
626 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
627 | return -EAGAIN; | ||
628 | |||
629 | __open_ok: | ||
630 | if (chip->hardware == SB_HW_ALS100) | ||
631 | runtime->hw.rate_max = 48000; | ||
632 | if (chip->mode & SB_RATE_LOCK) | ||
633 | runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; | ||
634 | chip->capture_substream = substream; | ||
635 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int snd_sb16_capture_close(snd_pcm_substream_t * substream) | ||
640 | { | ||
641 | unsigned long flags; | ||
642 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
643 | |||
644 | snd_sb16_csp_capture_close(chip); | ||
645 | spin_lock_irqsave(&chip->open_lock, flags); | ||
646 | chip->capture_substream = NULL; | ||
647 | chip->mode &= ~SB_MODE_CAPTURE; | ||
648 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | /* | ||
653 | * DMA control interface | ||
654 | */ | ||
655 | |||
656 | static int snd_sb16_set_dma_mode(sb_t *chip, int what) | ||
657 | { | ||
658 | if (chip->dma8 < 0 || chip->dma16 < 0) { | ||
659 | snd_assert(what == 0, return -EINVAL); | ||
660 | return 0; | ||
661 | } | ||
662 | if (what == 0) { | ||
663 | chip->force_mode16 = 0; | ||
664 | } else if (what == 1) { | ||
665 | chip->force_mode16 = SB_MODE_PLAYBACK_16; | ||
666 | } else if (what == 2) { | ||
667 | chip->force_mode16 = SB_MODE_CAPTURE_16; | ||
668 | } else { | ||
669 | return -EINVAL; | ||
670 | } | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static int snd_sb16_get_dma_mode(sb_t *chip) | ||
675 | { | ||
676 | if (chip->dma8 < 0 || chip->dma16 < 0) | ||
677 | return 0; | ||
678 | switch (chip->force_mode16) { | ||
679 | case SB_MODE_PLAYBACK_16: | ||
680 | return 1; | ||
681 | case SB_MODE_CAPTURE_16: | ||
682 | return 2; | ||
683 | default: | ||
684 | return 0; | ||
685 | } | ||
686 | } | ||
687 | |||
688 | static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
689 | { | ||
690 | static char *texts[3] = { | ||
691 | "Auto", "Playback", "Capture" | ||
692 | }; | ||
693 | |||
694 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
695 | uinfo->count = 1; | ||
696 | uinfo->value.enumerated.items = 3; | ||
697 | if (uinfo->value.enumerated.item > 2) | ||
698 | uinfo->value.enumerated.item = 2; | ||
699 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
704 | { | ||
705 | sb_t *chip = snd_kcontrol_chip(kcontrol); | ||
706 | unsigned long flags; | ||
707 | |||
708 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
709 | ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); | ||
710 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
715 | { | ||
716 | sb_t *chip = snd_kcontrol_chip(kcontrol); | ||
717 | unsigned long flags; | ||
718 | unsigned char nval, oval; | ||
719 | int change; | ||
720 | |||
721 | if ((nval = ucontrol->value.enumerated.item[0]) > 2) | ||
722 | return -EINVAL; | ||
723 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
724 | oval = snd_sb16_get_dma_mode(chip); | ||
725 | change = nval != oval; | ||
726 | snd_sb16_set_dma_mode(chip, nval); | ||
727 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
728 | return change; | ||
729 | } | ||
730 | |||
731 | static snd_kcontrol_new_t snd_sb16_dma_control = { | ||
732 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
733 | .name = "16-bit DMA Allocation", | ||
734 | .info = snd_sb16_dma_control_info, | ||
735 | .get = snd_sb16_dma_control_get, | ||
736 | .put = snd_sb16_dma_control_put | ||
737 | }; | ||
738 | |||
739 | /* | ||
740 | * Initialization part | ||
741 | */ | ||
742 | |||
743 | int snd_sb16dsp_configure(sb_t * chip) | ||
744 | { | ||
745 | unsigned long flags; | ||
746 | unsigned char irqreg = 0, dmareg = 0, mpureg; | ||
747 | unsigned char realirq, realdma, realmpureg; | ||
748 | /* note: mpu register should be present only on SB16 Vibra soundcards */ | ||
749 | |||
750 | // printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); | ||
751 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
752 | mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; | ||
753 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
754 | switch (chip->irq) { | ||
755 | case 2: | ||
756 | case 9: | ||
757 | irqreg |= SB_IRQSETUP_IRQ9; | ||
758 | break; | ||
759 | case 5: | ||
760 | irqreg |= SB_IRQSETUP_IRQ5; | ||
761 | break; | ||
762 | case 7: | ||
763 | irqreg |= SB_IRQSETUP_IRQ7; | ||
764 | break; | ||
765 | case 10: | ||
766 | irqreg |= SB_IRQSETUP_IRQ10; | ||
767 | break; | ||
768 | default: | ||
769 | return -EINVAL; | ||
770 | } | ||
771 | if (chip->dma8 >= 0) { | ||
772 | switch (chip->dma8) { | ||
773 | case 0: | ||
774 | dmareg |= SB_DMASETUP_DMA0; | ||
775 | break; | ||
776 | case 1: | ||
777 | dmareg |= SB_DMASETUP_DMA1; | ||
778 | break; | ||
779 | case 3: | ||
780 | dmareg |= SB_DMASETUP_DMA3; | ||
781 | break; | ||
782 | default: | ||
783 | return -EINVAL; | ||
784 | } | ||
785 | } | ||
786 | if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { | ||
787 | switch (chip->dma16) { | ||
788 | case 5: | ||
789 | dmareg |= SB_DMASETUP_DMA5; | ||
790 | break; | ||
791 | case 6: | ||
792 | dmareg |= SB_DMASETUP_DMA6; | ||
793 | break; | ||
794 | case 7: | ||
795 | dmareg |= SB_DMASETUP_DMA7; | ||
796 | break; | ||
797 | default: | ||
798 | return -EINVAL; | ||
799 | } | ||
800 | } | ||
801 | switch (chip->mpu_port) { | ||
802 | case 0x300: | ||
803 | mpureg |= 0x04; | ||
804 | break; | ||
805 | case 0x330: | ||
806 | mpureg |= 0x00; | ||
807 | break; | ||
808 | default: | ||
809 | mpureg |= 0x02; /* disable MPU */ | ||
810 | } | ||
811 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
812 | |||
813 | snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); | ||
814 | realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); | ||
815 | |||
816 | snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); | ||
817 | realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); | ||
818 | |||
819 | snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); | ||
820 | realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); | ||
821 | |||
822 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
823 | if ((~realirq) & irqreg || (~realdma) & dmareg) { | ||
824 | snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); | ||
825 | snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); | ||
826 | snd_printk("SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg); | ||
827 | return -ENODEV; | ||
828 | } | ||
829 | return 0; | ||
830 | } | ||
831 | |||
832 | static snd_pcm_ops_t snd_sb16_playback_ops = { | ||
833 | .open = snd_sb16_playback_open, | ||
834 | .close = snd_sb16_playback_close, | ||
835 | .ioctl = snd_pcm_lib_ioctl, | ||
836 | .hw_params = snd_sb16_hw_params, | ||
837 | .hw_free = snd_sb16_hw_free, | ||
838 | .prepare = snd_sb16_playback_prepare, | ||
839 | .trigger = snd_sb16_playback_trigger, | ||
840 | .pointer = snd_sb16_playback_pointer, | ||
841 | }; | ||
842 | |||
843 | static snd_pcm_ops_t snd_sb16_capture_ops = { | ||
844 | .open = snd_sb16_capture_open, | ||
845 | .close = snd_sb16_capture_close, | ||
846 | .ioctl = snd_pcm_lib_ioctl, | ||
847 | .hw_params = snd_sb16_hw_params, | ||
848 | .hw_free = snd_sb16_hw_free, | ||
849 | .prepare = snd_sb16_capture_prepare, | ||
850 | .trigger = snd_sb16_capture_trigger, | ||
851 | .pointer = snd_sb16_capture_pointer, | ||
852 | }; | ||
853 | |||
854 | static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm) | ||
855 | { | ||
856 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
857 | } | ||
858 | |||
859 | int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm) | ||
860 | { | ||
861 | snd_card_t *card = chip->card; | ||
862 | snd_pcm_t *pcm; | ||
863 | int err; | ||
864 | |||
865 | if (rpcm) | ||
866 | *rpcm = NULL; | ||
867 | if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) | ||
868 | return err; | ||
869 | sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); | ||
870 | pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
871 | pcm->private_data = chip; | ||
872 | pcm->private_free = snd_sb16dsp_pcm_free; | ||
873 | |||
874 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); | ||
875 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); | ||
876 | |||
877 | if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) | ||
878 | snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); | ||
879 | else | ||
880 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
881 | |||
882 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
883 | snd_dma_isa_data(), | ||
884 | 64*1024, 128*1024); | ||
885 | |||
886 | if (rpcm) | ||
887 | *rpcm = pcm; | ||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction) | ||
892 | { | ||
893 | return direction == SNDRV_PCM_STREAM_PLAYBACK ? | ||
894 | &snd_sb16_playback_ops : &snd_sb16_capture_ops; | ||
895 | } | ||
896 | |||
897 | EXPORT_SYMBOL(snd_sb16dsp_pcm); | ||
898 | EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); | ||
899 | EXPORT_SYMBOL(snd_sb16dsp_configure); | ||
900 | EXPORT_SYMBOL(snd_sb16dsp_interrupt); | ||
901 | |||
902 | /* | ||
903 | * INIT part | ||
904 | */ | ||
905 | |||
906 | static int __init alsa_sb16_init(void) | ||
907 | { | ||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | static void __exit alsa_sb16_exit(void) | ||
912 | { | ||
913 | } | ||
914 | |||
915 | module_init(alsa_sb16_init) | ||
916 | module_exit(alsa_sb16_exit) | ||
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c new file mode 100644 index 000000000000..e2cbc4202b3d --- /dev/null +++ b/sound/isa/sb/sb8.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/ioport.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/sb.h> | ||
29 | #include <sound/opl3.h> | ||
30 | #define SNDRV_LEGACY_AUTO_PROBE | ||
31 | #include <sound/initval.h> | ||
32 | |||
33 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
34 | MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}"); | ||
37 | |||
38 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
39 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
40 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
41 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ | ||
42 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
43 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */ | ||
44 | |||
45 | module_param_array(index, int, NULL, 0444); | ||
46 | MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard."); | ||
47 | module_param_array(id, charp, NULL, 0444); | ||
48 | MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard."); | ||
49 | module_param_array(enable, bool, NULL, 0444); | ||
50 | MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard."); | ||
51 | module_param_array(port, long, NULL, 0444); | ||
52 | MODULE_PARM_DESC(port, "Port # for SB8 driver."); | ||
53 | module_param_array(irq, int, NULL, 0444); | ||
54 | MODULE_PARM_DESC(irq, "IRQ # for SB8 driver."); | ||
55 | module_param_array(dma8, int, NULL, 0444); | ||
56 | MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver."); | ||
57 | |||
58 | struct snd_sb8 { | ||
59 | struct resource *fm_res; /* used to block FM i/o region for legacy cards */ | ||
60 | }; | ||
61 | |||
62 | static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
63 | |||
64 | static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
65 | { | ||
66 | sb_t *chip = dev_id; | ||
67 | |||
68 | if (chip->open & SB_OPEN_PCM) { | ||
69 | return snd_sb8dsp_interrupt(chip); | ||
70 | } else { | ||
71 | return snd_sb8dsp_midi_interrupt(chip); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static void snd_sb8_free(snd_card_t *card) | ||
76 | { | ||
77 | struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data; | ||
78 | |||
79 | if (acard == NULL) | ||
80 | return; | ||
81 | if (acard->fm_res) { | ||
82 | release_resource(acard->fm_res); | ||
83 | kfree_nocheck(acard->fm_res); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | static int __init snd_sb8_probe(int dev) | ||
88 | { | ||
89 | sb_t *chip; | ||
90 | snd_card_t *card; | ||
91 | struct snd_sb8 *acard; | ||
92 | opl3_t *opl3; | ||
93 | int err; | ||
94 | |||
95 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
96 | sizeof(struct snd_sb8)); | ||
97 | if (card == NULL) | ||
98 | return -ENOMEM; | ||
99 | acard = (struct snd_sb8 *)card->private_data; | ||
100 | card->private_free = snd_sb8_free; | ||
101 | |||
102 | /* block the 0x388 port to avoid PnP conflicts */ | ||
103 | acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); | ||
104 | |||
105 | if ((err = snd_sbdsp_create(card, port[dev], irq[dev], | ||
106 | snd_sb8_interrupt, | ||
107 | dma8[dev], | ||
108 | -1, | ||
109 | SB_HW_AUTO, | ||
110 | &chip)) < 0) { | ||
111 | snd_card_free(card); | ||
112 | return err; | ||
113 | } | ||
114 | if (chip->hardware >= SB_HW_16) { | ||
115 | snd_card_free(card); | ||
116 | if (chip->hardware == SB_HW_ALS100) | ||
117 | snd_printdd("ALS100 chip detected at 0x%lx, try snd-als100 module\n", | ||
118 | port[dev]); | ||
119 | else | ||
120 | snd_printdd("SB 16 chip detected at 0x%lx, try snd-sb16 module\n", | ||
121 | port[dev]); | ||
122 | return -ENODEV; | ||
123 | } | ||
124 | |||
125 | if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { | ||
126 | snd_card_free(card); | ||
127 | return err; | ||
128 | } | ||
129 | if ((err = snd_sbmixer_new(chip)) < 0) { | ||
130 | snd_card_free(card); | ||
131 | return err; | ||
132 | } | ||
133 | if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) { | ||
134 | if ((err = snd_opl3_create(card, chip->port + 8, 0, | ||
135 | OPL3_HW_AUTO, 1, | ||
136 | &opl3)) < 0) { | ||
137 | snd_printk(KERN_ERR "sb8: no OPL device at 0x%lx\n", chip->port + 8); | ||
138 | } | ||
139 | } else { | ||
140 | if ((err = snd_opl3_create(card, chip->port, chip->port + 2, | ||
141 | OPL3_HW_AUTO, 1, | ||
142 | &opl3)) < 0) { | ||
143 | snd_printk(KERN_ERR "sb8: no OPL device at 0x%lx-0x%lx\n", | ||
144 | chip->port, chip->port + 2); | ||
145 | } | ||
146 | } | ||
147 | if (err >= 0) { | ||
148 | if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { | ||
149 | snd_card_free(card); | ||
150 | return err; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { | ||
155 | snd_card_free(card); | ||
156 | return err; | ||
157 | } | ||
158 | |||
159 | strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8"); | ||
160 | strcpy(card->shortname, chip->name); | ||
161 | sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", | ||
162 | chip->name, | ||
163 | chip->port, | ||
164 | irq[dev], dma8[dev]); | ||
165 | if ((err = snd_card_register(card)) < 0) { | ||
166 | snd_card_free(card); | ||
167 | return err; | ||
168 | } | ||
169 | snd_sb8_cards[dev] = card; | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int __init snd_card_sb8_legacy_auto_probe(unsigned long xport) | ||
174 | { | ||
175 | static int dev; | ||
176 | int res; | ||
177 | |||
178 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
179 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
180 | continue; | ||
181 | port[dev] = xport; | ||
182 | res = snd_sb8_probe(dev); | ||
183 | if (res < 0) | ||
184 | port[dev] = SNDRV_AUTO_PORT; | ||
185 | return res; | ||
186 | } | ||
187 | return -ENODEV; | ||
188 | } | ||
189 | |||
190 | static int __init alsa_card_sb8_init(void) | ||
191 | { | ||
192 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; | ||
193 | int dev, cards, i; | ||
194 | |||
195 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
196 | if (port[dev] == SNDRV_AUTO_PORT) | ||
197 | continue; | ||
198 | if (snd_sb8_probe(dev) >= 0) | ||
199 | cards++; | ||
200 | } | ||
201 | i = snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe); | ||
202 | if (i > 0) | ||
203 | cards += i; | ||
204 | |||
205 | if (!cards) { | ||
206 | #ifdef MODULE | ||
207 | snd_printk(KERN_ERR "Sound Blaster soundcard not found or device busy\n"); | ||
208 | #endif | ||
209 | return -ENODEV; | ||
210 | } | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void __exit alsa_card_sb8_exit(void) | ||
215 | { | ||
216 | int idx; | ||
217 | |||
218 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
219 | snd_card_free(snd_sb8_cards[idx]); | ||
220 | } | ||
221 | |||
222 | module_init(alsa_card_sb8_init) | ||
223 | module_exit(alsa_card_sb8_exit) | ||
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c new file mode 100644 index 000000000000..87c9b1ba06cf --- /dev/null +++ b/sound/isa/sb/sb8_main.c | |||
@@ -0,0 +1,565 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Uros Bizjak <uros@kss-loka.si> | ||
4 | * | ||
5 | * Routines for control of 8-bit SoundBlaster cards and clones | ||
6 | * Please note: I don't have access to old SB8 soundcards. | ||
7 | * | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | * -- | ||
24 | * | ||
25 | * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> | ||
26 | * DSP can't respond to commands whilst in "high speed" mode. Caused | ||
27 | * glitching during playback. Fixed. | ||
28 | * | ||
29 | * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si> | ||
30 | * Cleaned up and rewrote lowlevel routines. | ||
31 | */ | ||
32 | |||
33 | #include <sound/driver.h> | ||
34 | #include <asm/io.h> | ||
35 | #include <asm/dma.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/time.h> | ||
38 | #include <sound/core.h> | ||
39 | #include <sound/sb.h> | ||
40 | |||
41 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Uros Bizjak <uros@kss-loka.si>"); | ||
42 | MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones"); | ||
43 | MODULE_LICENSE("GPL"); | ||
44 | |||
45 | #define SB8_CLOCK 1000000 | ||
46 | #define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) | ||
47 | #define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) | ||
48 | |||
49 | static ratnum_t clock = { | ||
50 | .num = SB8_CLOCK, | ||
51 | .den_min = 1, | ||
52 | .den_max = 256, | ||
53 | .den_step = 1, | ||
54 | }; | ||
55 | |||
56 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = { | ||
57 | .nrats = 1, | ||
58 | .rats = &clock, | ||
59 | }; | ||
60 | |||
61 | static ratnum_t stereo_clocks[] = { | ||
62 | { | ||
63 | .num = SB8_CLOCK, | ||
64 | .den_min = SB8_DEN(22050), | ||
65 | .den_max = SB8_DEN(22050), | ||
66 | .den_step = 1, | ||
67 | }, | ||
68 | { | ||
69 | .num = SB8_CLOCK, | ||
70 | .den_min = SB8_DEN(11025), | ||
71 | .den_max = SB8_DEN(11025), | ||
72 | .den_step = 1, | ||
73 | } | ||
74 | }; | ||
75 | |||
76 | static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params, | ||
77 | snd_pcm_hw_rule_t *rule) | ||
78 | { | ||
79 | snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
80 | if (c->min > 1) { | ||
81 | unsigned int num = 0, den = 0; | ||
82 | int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), | ||
83 | 2, stereo_clocks, &num, &den); | ||
84 | if (err >= 0 && den) { | ||
85 | params->rate_num = num; | ||
86 | params->rate_den = den; | ||
87 | } | ||
88 | return err; | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params, | ||
94 | snd_pcm_hw_rule_t *rule) | ||
95 | { | ||
96 | snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
97 | if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { | ||
98 | snd_interval_t t = { .min = 1, .max = 1 }; | ||
99 | return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream) | ||
105 | { | ||
106 | unsigned long flags; | ||
107 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
108 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
109 | unsigned int mixreg, rate, size, count; | ||
110 | |||
111 | rate = runtime->rate; | ||
112 | switch (chip->hardware) { | ||
113 | case SB_HW_PRO: | ||
114 | if (runtime->channels > 1) { | ||
115 | snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); | ||
116 | chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; | ||
117 | break; | ||
118 | } | ||
119 | /* fallthru */ | ||
120 | case SB_HW_201: | ||
121 | if (rate > 23000) { | ||
122 | chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; | ||
123 | break; | ||
124 | } | ||
125 | /* fallthru */ | ||
126 | case SB_HW_20: | ||
127 | chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; | ||
128 | break; | ||
129 | case SB_HW_10: | ||
130 | chip->playback_format = SB_DSP_OUTPUT; | ||
131 | break; | ||
132 | default: | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
136 | count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); | ||
137 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
138 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); | ||
139 | if (runtime->channels > 1) { | ||
140 | /* set playback stereo mode */ | ||
141 | spin_lock(&chip->mixer_lock); | ||
142 | mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); | ||
143 | snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); | ||
144 | spin_unlock(&chip->mixer_lock); | ||
145 | |||
146 | /* Soundblaster hardware programming reference guide, 3-23 */ | ||
147 | snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); | ||
148 | runtime->dma_area[0] = 0x80; | ||
149 | snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE); | ||
150 | /* force interrupt */ | ||
151 | chip->mode = SB_MODE_HALT; | ||
152 | snd_sbdsp_command(chip, SB_DSP_OUTPUT); | ||
153 | snd_sbdsp_command(chip, 0); | ||
154 | snd_sbdsp_command(chip, 0); | ||
155 | } | ||
156 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); | ||
157 | if (runtime->channels > 1) { | ||
158 | snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); | ||
159 | spin_lock(&chip->mixer_lock); | ||
160 | /* save output filter status and turn it off */ | ||
161 | mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); | ||
162 | snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); | ||
163 | spin_unlock(&chip->mixer_lock); | ||
164 | /* just use force_mode16 for temporary storate... */ | ||
165 | chip->force_mode16 = mixreg; | ||
166 | } else { | ||
167 | snd_sbdsp_command(chip, 256 - runtime->rate_den); | ||
168 | } | ||
169 | if (chip->playback_format != SB_DSP_OUTPUT) { | ||
170 | count--; | ||
171 | snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); | ||
172 | snd_sbdsp_command(chip, count & 0xff); | ||
173 | snd_sbdsp_command(chip, count >> 8); | ||
174 | } | ||
175 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
176 | snd_dma_program(chip->dma8, runtime->dma_addr, | ||
177 | size, DMA_MODE_WRITE | DMA_AUTOINIT); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream, | ||
182 | int cmd) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
186 | unsigned int count; | ||
187 | |||
188 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
189 | switch (cmd) { | ||
190 | case SNDRV_PCM_TRIGGER_START: | ||
191 | snd_sbdsp_command(chip, chip->playback_format); | ||
192 | if (chip->playback_format == SB_DSP_OUTPUT) { | ||
193 | count = chip->p_period_size - 1; | ||
194 | snd_sbdsp_command(chip, count & 0xff); | ||
195 | snd_sbdsp_command(chip, count >> 8); | ||
196 | } | ||
197 | break; | ||
198 | case SNDRV_PCM_TRIGGER_STOP: | ||
199 | if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) { | ||
200 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
201 | snd_sbdsp_reset(chip); | ||
202 | if (runtime->channels > 1) { | ||
203 | spin_lock(&chip->mixer_lock); | ||
204 | /* restore output filter and set hardware to mono mode */ | ||
205 | snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02); | ||
206 | spin_unlock(&chip->mixer_lock); | ||
207 | } | ||
208 | } else { | ||
209 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
210 | } | ||
211 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); | ||
212 | } | ||
213 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
214 | chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT; | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int snd_sb8_hw_params(snd_pcm_substream_t * substream, | ||
219 | snd_pcm_hw_params_t * hw_params) | ||
220 | { | ||
221 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
222 | } | ||
223 | |||
224 | static int snd_sb8_hw_free(snd_pcm_substream_t * substream) | ||
225 | { | ||
226 | snd_pcm_lib_free_pages(substream); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream) | ||
231 | { | ||
232 | unsigned long flags; | ||
233 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
234 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
235 | unsigned int mixreg, rate, size, count; | ||
236 | |||
237 | rate = runtime->rate; | ||
238 | switch (chip->hardware) { | ||
239 | case SB_HW_PRO: | ||
240 | if (runtime->channels > 1) { | ||
241 | snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); | ||
242 | chip->capture_format = SB_DSP_HI_INPUT_AUTO; | ||
243 | break; | ||
244 | } | ||
245 | chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO; | ||
246 | break; | ||
247 | case SB_HW_201: | ||
248 | if (rate > 13000) { | ||
249 | chip->capture_format = SB_DSP_HI_INPUT_AUTO; | ||
250 | break; | ||
251 | } | ||
252 | /* fallthru */ | ||
253 | case SB_HW_20: | ||
254 | chip->capture_format = SB_DSP_LO_INPUT_AUTO; | ||
255 | break; | ||
256 | case SB_HW_10: | ||
257 | chip->capture_format = SB_DSP_INPUT; | ||
258 | break; | ||
259 | default: | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
263 | count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); | ||
264 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
265 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); | ||
266 | if (runtime->channels > 1) | ||
267 | snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); | ||
268 | snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); | ||
269 | if (runtime->channels > 1) { | ||
270 | snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); | ||
271 | spin_lock(&chip->mixer_lock); | ||
272 | /* save input filter status and turn it off */ | ||
273 | mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT); | ||
274 | snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20); | ||
275 | spin_unlock(&chip->mixer_lock); | ||
276 | /* just use force_mode16 for temporary storate... */ | ||
277 | chip->force_mode16 = mixreg; | ||
278 | } else { | ||
279 | snd_sbdsp_command(chip, 256 - runtime->rate_den); | ||
280 | } | ||
281 | if (chip->capture_format != SB_DSP_OUTPUT) { | ||
282 | count--; | ||
283 | snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); | ||
284 | snd_sbdsp_command(chip, count & 0xff); | ||
285 | snd_sbdsp_command(chip, count >> 8); | ||
286 | } | ||
287 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
288 | snd_dma_program(chip->dma8, runtime->dma_addr, | ||
289 | size, DMA_MODE_READ | DMA_AUTOINIT); | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream, | ||
294 | int cmd) | ||
295 | { | ||
296 | unsigned long flags; | ||
297 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
298 | unsigned int count; | ||
299 | |||
300 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
301 | switch (cmd) { | ||
302 | case SNDRV_PCM_TRIGGER_START: | ||
303 | snd_sbdsp_command(chip, chip->capture_format); | ||
304 | if (chip->capture_format == SB_DSP_INPUT) { | ||
305 | count = chip->c_period_size - 1; | ||
306 | snd_sbdsp_command(chip, count & 0xff); | ||
307 | snd_sbdsp_command(chip, count >> 8); | ||
308 | } | ||
309 | break; | ||
310 | case SNDRV_PCM_TRIGGER_STOP: | ||
311 | if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) { | ||
312 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
313 | snd_sbdsp_reset(chip); | ||
314 | if (runtime->channels > 1) { | ||
315 | /* restore input filter status */ | ||
316 | spin_lock(&chip->mixer_lock); | ||
317 | snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16); | ||
318 | spin_unlock(&chip->mixer_lock); | ||
319 | /* set hardware to mono mode */ | ||
320 | snd_sbdsp_command(chip, SB_DSP_MONO_8BIT); | ||
321 | } | ||
322 | } else { | ||
323 | snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); | ||
324 | } | ||
325 | snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); | ||
326 | } | ||
327 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
328 | chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | irqreturn_t snd_sb8dsp_interrupt(sb_t *chip) | ||
333 | { | ||
334 | snd_pcm_substream_t *substream; | ||
335 | snd_pcm_runtime_t *runtime; | ||
336 | |||
337 | #if 0 | ||
338 | snd_printk("sb8: interrupt\n"); | ||
339 | #endif | ||
340 | snd_sb_ack_8bit(chip); | ||
341 | switch (chip->mode) { | ||
342 | case SB_MODE_PLAYBACK_8: /* ok.. playback is active */ | ||
343 | substream = chip->playback_substream; | ||
344 | runtime = substream->runtime; | ||
345 | if (chip->playback_format == SB_DSP_OUTPUT) | ||
346 | snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); | ||
347 | snd_pcm_period_elapsed(substream); | ||
348 | break; | ||
349 | case SB_MODE_CAPTURE_8: | ||
350 | substream = chip->capture_substream; | ||
351 | runtime = substream->runtime; | ||
352 | if (chip->capture_format == SB_DSP_INPUT) | ||
353 | snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); | ||
354 | snd_pcm_period_elapsed(substream); | ||
355 | break; | ||
356 | } | ||
357 | return IRQ_HANDLED; | ||
358 | } | ||
359 | |||
360 | static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream) | ||
361 | { | ||
362 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
363 | size_t ptr; | ||
364 | |||
365 | if (chip->mode != SB_MODE_PLAYBACK_8) | ||
366 | return 0; | ||
367 | ptr = snd_dma_pointer(chip->dma8, chip->p_dma_size); | ||
368 | return bytes_to_frames(substream->runtime, ptr); | ||
369 | } | ||
370 | |||
371 | static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream) | ||
372 | { | ||
373 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
374 | size_t ptr; | ||
375 | |||
376 | if (chip->mode != SB_MODE_CAPTURE_8) | ||
377 | return 0; | ||
378 | ptr = snd_dma_pointer(chip->dma8, chip->c_dma_size); | ||
379 | return bytes_to_frames(substream->runtime, ptr); | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | |||
384 | */ | ||
385 | |||
386 | static snd_pcm_hardware_t snd_sb8_playback = | ||
387 | { | ||
388 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
389 | SNDRV_PCM_INFO_MMAP_VALID), | ||
390 | .formats = SNDRV_PCM_FMTBIT_U8, | ||
391 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | | ||
392 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), | ||
393 | .rate_min = 4000, | ||
394 | .rate_max = 23000, | ||
395 | .channels_min = 1, | ||
396 | .channels_max = 1, | ||
397 | .buffer_bytes_max = 65536, | ||
398 | .period_bytes_min = 64, | ||
399 | .period_bytes_max = 65536, | ||
400 | .periods_min = 1, | ||
401 | .periods_max = 1024, | ||
402 | .fifo_size = 0, | ||
403 | }; | ||
404 | |||
405 | static snd_pcm_hardware_t snd_sb8_capture = | ||
406 | { | ||
407 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
408 | SNDRV_PCM_INFO_MMAP_VALID), | ||
409 | .formats = SNDRV_PCM_FMTBIT_U8, | ||
410 | .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | | ||
411 | SNDRV_PCM_RATE_11025), | ||
412 | .rate_min = 4000, | ||
413 | .rate_max = 13000, | ||
414 | .channels_min = 1, | ||
415 | .channels_max = 1, | ||
416 | .buffer_bytes_max = 65536, | ||
417 | .period_bytes_min = 64, | ||
418 | .period_bytes_max = 65536, | ||
419 | .periods_min = 1, | ||
420 | .periods_max = 1024, | ||
421 | .fifo_size = 0, | ||
422 | }; | ||
423 | |||
424 | /* | ||
425 | * | ||
426 | */ | ||
427 | |||
428 | static int snd_sb8_open(snd_pcm_substream_t *substream) | ||
429 | { | ||
430 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
431 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
432 | unsigned long flags; | ||
433 | |||
434 | spin_lock_irqsave(&chip->open_lock, flags); | ||
435 | if (chip->open) { | ||
436 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
437 | return -EAGAIN; | ||
438 | } | ||
439 | chip->open |= SB_OPEN_PCM; | ||
440 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
441 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
442 | chip->playback_substream = substream; | ||
443 | runtime->hw = snd_sb8_playback; | ||
444 | } else { | ||
445 | chip->capture_substream = substream; | ||
446 | runtime->hw = snd_sb8_capture; | ||
447 | } | ||
448 | switch (chip->hardware) { | ||
449 | case SB_HW_PRO: | ||
450 | runtime->hw.rate_max = 44100; | ||
451 | runtime->hw.channels_max = 2; | ||
452 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
453 | snd_sb8_hw_constraint_rate_channels, NULL, | ||
454 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
455 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
456 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
457 | snd_sb8_hw_constraint_channels_rate, NULL, | ||
458 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
459 | break; | ||
460 | case SB_HW_201: | ||
461 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
462 | runtime->hw.rate_max = 44100; | ||
463 | } else { | ||
464 | runtime->hw.rate_max = 15000; | ||
465 | } | ||
466 | default: | ||
467 | break; | ||
468 | } | ||
469 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
470 | &hw_constraints_clock); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int snd_sb8_close(snd_pcm_substream_t *substream) | ||
475 | { | ||
476 | unsigned long flags; | ||
477 | sb_t *chip = snd_pcm_substream_chip(substream); | ||
478 | |||
479 | chip->playback_substream = NULL; | ||
480 | chip->capture_substream = NULL; | ||
481 | spin_lock_irqsave(&chip->open_lock, flags); | ||
482 | chip->open &= ~SB_OPEN_PCM; | ||
483 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | /* | ||
488 | * Initialization part | ||
489 | */ | ||
490 | |||
491 | static snd_pcm_ops_t snd_sb8_playback_ops = { | ||
492 | .open = snd_sb8_open, | ||
493 | .close = snd_sb8_close, | ||
494 | .ioctl = snd_pcm_lib_ioctl, | ||
495 | .hw_params = snd_sb8_hw_params, | ||
496 | .hw_free = snd_sb8_hw_free, | ||
497 | .prepare = snd_sb8_playback_prepare, | ||
498 | .trigger = snd_sb8_playback_trigger, | ||
499 | .pointer = snd_sb8_playback_pointer, | ||
500 | }; | ||
501 | |||
502 | static snd_pcm_ops_t snd_sb8_capture_ops = { | ||
503 | .open = snd_sb8_open, | ||
504 | .close = snd_sb8_close, | ||
505 | .ioctl = snd_pcm_lib_ioctl, | ||
506 | .hw_params = snd_sb8_hw_params, | ||
507 | .hw_free = snd_sb8_hw_free, | ||
508 | .prepare = snd_sb8_capture_prepare, | ||
509 | .trigger = snd_sb8_capture_trigger, | ||
510 | .pointer = snd_sb8_capture_pointer, | ||
511 | }; | ||
512 | |||
513 | static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm) | ||
514 | { | ||
515 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
516 | } | ||
517 | |||
518 | int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm) | ||
519 | { | ||
520 | snd_card_t *card = chip->card; | ||
521 | snd_pcm_t *pcm; | ||
522 | int err; | ||
523 | |||
524 | if (rpcm) | ||
525 | *rpcm = NULL; | ||
526 | if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) | ||
527 | return err; | ||
528 | sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); | ||
529 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
530 | pcm->private_data = chip; | ||
531 | pcm->private_free = snd_sb8dsp_pcm_free; | ||
532 | |||
533 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); | ||
534 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); | ||
535 | |||
536 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
537 | snd_dma_isa_data(), | ||
538 | 64*1024, 64*1024); | ||
539 | |||
540 | if (rpcm) | ||
541 | *rpcm = pcm; | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | EXPORT_SYMBOL(snd_sb8dsp_pcm); | ||
546 | EXPORT_SYMBOL(snd_sb8dsp_interrupt); | ||
547 | /* sb8_midi.c */ | ||
548 | EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); | ||
549 | EXPORT_SYMBOL(snd_sb8dsp_midi); | ||
550 | |||
551 | /* | ||
552 | * INIT part | ||
553 | */ | ||
554 | |||
555 | static int __init alsa_sb8_init(void) | ||
556 | { | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | static void __exit alsa_sb8_exit(void) | ||
561 | { | ||
562 | } | ||
563 | |||
564 | module_init(alsa_sb8_init) | ||
565 | module_exit(alsa_sb8_exit) | ||
diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c new file mode 100644 index 000000000000..d2c633a40e74 --- /dev/null +++ b/sound/isa/sb/sb8_midi.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of SoundBlaster cards - MIDI interface | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * -- | ||
20 | * | ||
21 | * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> | ||
22 | * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from | ||
23 | * working. | ||
24 | * | ||
25 | * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de> | ||
26 | * Added full duplex UART mode for DSP version 2.0 and later. | ||
27 | */ | ||
28 | |||
29 | #include <sound/driver.h> | ||
30 | #include <asm/io.h> | ||
31 | #include <linux/time.h> | ||
32 | #include <sound/core.h> | ||
33 | #include <sound/sb.h> | ||
34 | |||
35 | /* | ||
36 | |||
37 | */ | ||
38 | |||
39 | irqreturn_t snd_sb8dsp_midi_interrupt(sb_t * chip) | ||
40 | { | ||
41 | snd_rawmidi_t *rmidi; | ||
42 | int max = 64; | ||
43 | char byte; | ||
44 | |||
45 | if (chip == NULL || (rmidi = chip->rmidi) == NULL) { | ||
46 | inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ | ||
47 | return IRQ_NONE; | ||
48 | } | ||
49 | spin_lock(&chip->midi_input_lock); | ||
50 | while (max-- > 0) { | ||
51 | if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { | ||
52 | byte = inb(SBP(chip, READ)); | ||
53 | if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { | ||
54 | snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | spin_unlock(&chip->midi_input_lock); | ||
59 | return IRQ_HANDLED; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | |||
64 | */ | ||
65 | |||
66 | static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream) | ||
67 | { | ||
68 | unsigned long flags; | ||
69 | sb_t *chip; | ||
70 | unsigned int valid_open_flags; | ||
71 | |||
72 | chip = substream->rmidi->private_data; | ||
73 | valid_open_flags = chip->hardware >= SB_HW_20 | ||
74 | ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; | ||
75 | spin_lock_irqsave(&chip->open_lock, flags); | ||
76 | if (chip->open & ~valid_open_flags) { | ||
77 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
78 | return -EAGAIN; | ||
79 | } | ||
80 | chip->open |= SB_OPEN_MIDI_INPUT; | ||
81 | chip->midi_substream_input = substream; | ||
82 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { | ||
83 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
84 | snd_sbdsp_reset(chip); /* reset DSP */ | ||
85 | if (chip->hardware >= SB_HW_20) | ||
86 | snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); | ||
87 | } else { | ||
88 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream) | ||
94 | { | ||
95 | unsigned long flags; | ||
96 | sb_t *chip; | ||
97 | unsigned int valid_open_flags; | ||
98 | |||
99 | chip = substream->rmidi->private_data; | ||
100 | valid_open_flags = chip->hardware >= SB_HW_20 | ||
101 | ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; | ||
102 | spin_lock_irqsave(&chip->open_lock, flags); | ||
103 | if (chip->open & ~valid_open_flags) { | ||
104 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
105 | return -EAGAIN; | ||
106 | } | ||
107 | chip->open |= SB_OPEN_MIDI_OUTPUT; | ||
108 | chip->midi_substream_output = substream; | ||
109 | if (!(chip->open & SB_OPEN_MIDI_INPUT)) { | ||
110 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
111 | snd_sbdsp_reset(chip); /* reset DSP */ | ||
112 | if (chip->hardware >= SB_HW_20) | ||
113 | snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); | ||
114 | } else { | ||
115 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
116 | } | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream) | ||
121 | { | ||
122 | unsigned long flags; | ||
123 | sb_t *chip; | ||
124 | |||
125 | chip = substream->rmidi->private_data; | ||
126 | spin_lock_irqsave(&chip->open_lock, flags); | ||
127 | chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); | ||
128 | chip->midi_substream_input = NULL; | ||
129 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { | ||
130 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
131 | snd_sbdsp_reset(chip); /* reset DSP */ | ||
132 | } else { | ||
133 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
134 | } | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream) | ||
139 | { | ||
140 | unsigned long flags; | ||
141 | sb_t *chip; | ||
142 | |||
143 | chip = substream->rmidi->private_data; | ||
144 | spin_lock_irqsave(&chip->open_lock, flags); | ||
145 | chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); | ||
146 | chip->midi_substream_output = NULL; | ||
147 | if (!(chip->open & SB_OPEN_MIDI_INPUT)) { | ||
148 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
149 | snd_sbdsp_reset(chip); /* reset DSP */ | ||
150 | } else { | ||
151 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
152 | } | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
157 | { | ||
158 | unsigned long flags; | ||
159 | sb_t *chip; | ||
160 | |||
161 | chip = substream->rmidi->private_data; | ||
162 | spin_lock_irqsave(&chip->open_lock, flags); | ||
163 | if (up) { | ||
164 | if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { | ||
165 | if (chip->hardware < SB_HW_20) | ||
166 | snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); | ||
167 | chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; | ||
168 | } | ||
169 | } else { | ||
170 | if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { | ||
171 | if (chip->hardware < SB_HW_20) | ||
172 | snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); | ||
173 | chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; | ||
174 | } | ||
175 | } | ||
176 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
177 | } | ||
178 | |||
179 | static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream) | ||
180 | { | ||
181 | unsigned long flags; | ||
182 | sb_t *chip; | ||
183 | char byte; | ||
184 | int max = 32; | ||
185 | |||
186 | /* how big is Tx FIFO? */ | ||
187 | chip = substream->rmidi->private_data; | ||
188 | while (max-- > 0) { | ||
189 | spin_lock_irqsave(&chip->open_lock, flags); | ||
190 | if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { | ||
191 | chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; | ||
192 | del_timer(&chip->midi_timer); | ||
193 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
194 | break; | ||
195 | } | ||
196 | if (chip->hardware >= SB_HW_20) { | ||
197 | int timeout = 8; | ||
198 | while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) | ||
199 | ; | ||
200 | if (timeout == 0) { | ||
201 | /* Tx FIFO full - try again later */ | ||
202 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
203 | break; | ||
204 | } | ||
205 | outb(byte, SBP(chip, WRITE)); | ||
206 | } else { | ||
207 | snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); | ||
208 | snd_sbdsp_command(chip, byte); | ||
209 | } | ||
210 | snd_rawmidi_transmit_ack(substream, 1); | ||
211 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | static void snd_sb8dsp_midi_output_timer(unsigned long data) | ||
216 | { | ||
217 | snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data; | ||
218 | sb_t * chip = substream->rmidi->private_data; | ||
219 | unsigned long flags; | ||
220 | |||
221 | spin_lock_irqsave(&chip->open_lock, flags); | ||
222 | chip->midi_timer.expires = 1 + jiffies; | ||
223 | add_timer(&chip->midi_timer); | ||
224 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
225 | snd_sb8dsp_midi_output_write(substream); | ||
226 | } | ||
227 | |||
228 | static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
229 | { | ||
230 | unsigned long flags; | ||
231 | sb_t *chip; | ||
232 | |||
233 | chip = substream->rmidi->private_data; | ||
234 | spin_lock_irqsave(&chip->open_lock, flags); | ||
235 | if (up) { | ||
236 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { | ||
237 | init_timer(&chip->midi_timer); | ||
238 | chip->midi_timer.function = snd_sb8dsp_midi_output_timer; | ||
239 | chip->midi_timer.data = (unsigned long) substream; | ||
240 | chip->midi_timer.expires = 1 + jiffies; | ||
241 | add_timer(&chip->midi_timer); | ||
242 | chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; | ||
243 | } | ||
244 | } else { | ||
245 | if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { | ||
246 | chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; | ||
247 | } | ||
248 | } | ||
249 | spin_unlock_irqrestore(&chip->open_lock, flags); | ||
250 | |||
251 | if (up) | ||
252 | snd_sb8dsp_midi_output_write(substream); | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | |||
257 | */ | ||
258 | |||
259 | static snd_rawmidi_ops_t snd_sb8dsp_midi_output = | ||
260 | { | ||
261 | .open = snd_sb8dsp_midi_output_open, | ||
262 | .close = snd_sb8dsp_midi_output_close, | ||
263 | .trigger = snd_sb8dsp_midi_output_trigger, | ||
264 | }; | ||
265 | |||
266 | static snd_rawmidi_ops_t snd_sb8dsp_midi_input = | ||
267 | { | ||
268 | .open = snd_sb8dsp_midi_input_open, | ||
269 | .close = snd_sb8dsp_midi_input_close, | ||
270 | .trigger = snd_sb8dsp_midi_input_trigger, | ||
271 | }; | ||
272 | |||
273 | int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi) | ||
274 | { | ||
275 | snd_rawmidi_t *rmidi; | ||
276 | int err; | ||
277 | |||
278 | if (rrawmidi) | ||
279 | *rrawmidi = NULL; | ||
280 | if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) | ||
281 | return err; | ||
282 | strcpy(rmidi->name, "SB8 MIDI"); | ||
283 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); | ||
284 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); | ||
285 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; | ||
286 | if (chip->hardware >= SB_HW_20) | ||
287 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; | ||
288 | rmidi->private_data = chip; | ||
289 | chip->rmidi = rmidi; | ||
290 | if (rrawmidi) | ||
291 | *rrawmidi = rmidi; | ||
292 | return 0; | ||
293 | } | ||
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c new file mode 100644 index 000000000000..5b6bde213ea0 --- /dev/null +++ b/sound/isa/sb/sb_common.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Uros Bizjak <uros@kss-loka.si> | ||
4 | * | ||
5 | * Lowlevel routines for control of Sound Blaster cards | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/sb.h> | ||
31 | #include <sound/initval.h> | ||
32 | |||
33 | #include <asm/io.h> | ||
34 | #include <asm/dma.h> | ||
35 | |||
36 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
37 | MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | #define BUSY_LOOPS 100000 | ||
41 | |||
42 | #undef IO_DEBUG | ||
43 | |||
44 | int snd_sbdsp_command(sb_t *chip, unsigned char val) | ||
45 | { | ||
46 | int i; | ||
47 | #ifdef IO_DEBUG | ||
48 | snd_printk("command 0x%x\n", val); | ||
49 | #endif | ||
50 | for (i = BUSY_LOOPS; i; i--) | ||
51 | if ((inb(SBP(chip, STATUS)) & 0x80) == 0) { | ||
52 | outb(val, SBP(chip, COMMAND)); | ||
53 | return 1; | ||
54 | } | ||
55 | snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | int snd_sbdsp_get_byte(sb_t *chip) | ||
60 | { | ||
61 | int val; | ||
62 | int i; | ||
63 | for (i = BUSY_LOOPS; i; i--) { | ||
64 | if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { | ||
65 | val = inb(SBP(chip, READ)); | ||
66 | #ifdef IO_DEBUG | ||
67 | snd_printk("get_byte 0x%x\n", val); | ||
68 | #endif | ||
69 | return val; | ||
70 | } | ||
71 | } | ||
72 | snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port); | ||
73 | return -ENODEV; | ||
74 | } | ||
75 | |||
76 | int snd_sbdsp_reset(sb_t *chip) | ||
77 | { | ||
78 | int i; | ||
79 | |||
80 | outb(1, SBP(chip, RESET)); | ||
81 | udelay(10); | ||
82 | outb(0, SBP(chip, RESET)); | ||
83 | udelay(30); | ||
84 | for (i = BUSY_LOOPS; i; i--) | ||
85 | if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { | ||
86 | if (inb(SBP(chip, READ)) == 0xaa) | ||
87 | return 0; | ||
88 | else | ||
89 | break; | ||
90 | } | ||
91 | snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port); | ||
92 | return -ENODEV; | ||
93 | } | ||
94 | |||
95 | static int snd_sbdsp_version(sb_t * chip) | ||
96 | { | ||
97 | unsigned int result = -ENODEV; | ||
98 | |||
99 | snd_sbdsp_command(chip, SB_DSP_GET_VERSION); | ||
100 | result = (short) snd_sbdsp_get_byte(chip) << 8; | ||
101 | result |= (short) snd_sbdsp_get_byte(chip); | ||
102 | return result; | ||
103 | } | ||
104 | |||
105 | static int snd_sbdsp_probe(sb_t * chip) | ||
106 | { | ||
107 | int version; | ||
108 | int major, minor; | ||
109 | char *str; | ||
110 | unsigned long flags; | ||
111 | |||
112 | /* | ||
113 | * initialization sequence | ||
114 | */ | ||
115 | |||
116 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
117 | if (snd_sbdsp_reset(chip) < 0) { | ||
118 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
119 | return -ENODEV; | ||
120 | } | ||
121 | version = snd_sbdsp_version(chip); | ||
122 | if (version < 0) { | ||
123 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
124 | return -ENODEV; | ||
125 | } | ||
126 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
127 | major = version >> 8; | ||
128 | minor = version & 0xff; | ||
129 | snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", | ||
130 | chip->port, major, minor); | ||
131 | |||
132 | switch (chip->hardware) { | ||
133 | case SB_HW_AUTO: | ||
134 | switch (major) { | ||
135 | case 1: | ||
136 | chip->hardware = SB_HW_10; | ||
137 | str = "1.0"; | ||
138 | break; | ||
139 | case 2: | ||
140 | if (minor) { | ||
141 | chip->hardware = SB_HW_201; | ||
142 | str = "2.01+"; | ||
143 | } else { | ||
144 | chip->hardware = SB_HW_20; | ||
145 | str = "2.0"; | ||
146 | } | ||
147 | break; | ||
148 | case 3: | ||
149 | chip->hardware = SB_HW_PRO; | ||
150 | str = "Pro"; | ||
151 | break; | ||
152 | case 4: | ||
153 | chip->hardware = SB_HW_16; | ||
154 | str = "16"; | ||
155 | break; | ||
156 | default: | ||
157 | snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n", | ||
158 | chip->port, major, minor); | ||
159 | return -ENODEV; | ||
160 | } | ||
161 | break; | ||
162 | case SB_HW_ALS100: | ||
163 | str = "16 (ALS-100)"; | ||
164 | break; | ||
165 | case SB_HW_ALS4000: | ||
166 | str = "16 (ALS-4000)"; | ||
167 | break; | ||
168 | case SB_HW_DT019X: | ||
169 | str = "(DT019X/ALS007)"; | ||
170 | break; | ||
171 | default: | ||
172 | return -ENODEV; | ||
173 | } | ||
174 | sprintf(chip->name, "Sound Blaster %s", str); | ||
175 | chip->version = (major << 8) | minor; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int snd_sbdsp_free(sb_t *chip) | ||
180 | { | ||
181 | if (chip->res_port) { | ||
182 | release_resource(chip->res_port); | ||
183 | kfree_nocheck(chip->res_port); | ||
184 | } | ||
185 | if (chip->irq >= 0) | ||
186 | free_irq(chip->irq, (void *) chip); | ||
187 | #ifdef CONFIG_ISA | ||
188 | if (chip->dma8 >= 0) { | ||
189 | disable_dma(chip->dma8); | ||
190 | free_dma(chip->dma8); | ||
191 | } | ||
192 | if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { | ||
193 | disable_dma(chip->dma16); | ||
194 | free_dma(chip->dma16); | ||
195 | } | ||
196 | #endif | ||
197 | kfree(chip); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int snd_sbdsp_dev_free(snd_device_t *device) | ||
202 | { | ||
203 | sb_t *chip = device->device_data; | ||
204 | return snd_sbdsp_free(chip); | ||
205 | } | ||
206 | |||
207 | int snd_sbdsp_create(snd_card_t *card, | ||
208 | unsigned long port, | ||
209 | int irq, | ||
210 | irqreturn_t (*irq_handler)(int, void *, struct pt_regs *), | ||
211 | int dma8, | ||
212 | int dma16, | ||
213 | unsigned short hardware, | ||
214 | sb_t **r_chip) | ||
215 | { | ||
216 | sb_t *chip; | ||
217 | int err; | ||
218 | static snd_device_ops_t ops = { | ||
219 | .dev_free = snd_sbdsp_dev_free, | ||
220 | }; | ||
221 | |||
222 | snd_assert(r_chip != NULL, return -EINVAL); | ||
223 | *r_chip = NULL; | ||
224 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
225 | if (chip == NULL) | ||
226 | return -ENOMEM; | ||
227 | spin_lock_init(&chip->reg_lock); | ||
228 | spin_lock_init(&chip->open_lock); | ||
229 | spin_lock_init(&chip->midi_input_lock); | ||
230 | spin_lock_init(&chip->mixer_lock); | ||
231 | chip->irq = -1; | ||
232 | chip->dma8 = -1; | ||
233 | chip->dma16 = -1; | ||
234 | chip->port = port; | ||
235 | |||
236 | if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ? | ||
237 | SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT, | ||
238 | "SoundBlaster", (void *) chip)) { | ||
239 | snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq); | ||
240 | snd_sbdsp_free(chip); | ||
241 | return -EBUSY; | ||
242 | } | ||
243 | chip->irq = irq; | ||
244 | |||
245 | if (hardware == SB_HW_ALS4000) | ||
246 | goto __skip_allocation; | ||
247 | |||
248 | if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) { | ||
249 | snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port); | ||
250 | snd_sbdsp_free(chip); | ||
251 | return -EBUSY; | ||
252 | } | ||
253 | |||
254 | #ifdef CONFIG_ISA | ||
255 | if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) { | ||
256 | snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8); | ||
257 | snd_sbdsp_free(chip); | ||
258 | return -EBUSY; | ||
259 | } | ||
260 | chip->dma8 = dma8; | ||
261 | if (dma16 >= 0) { | ||
262 | if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) { | ||
263 | /* no duplex */ | ||
264 | dma16 = -1; | ||
265 | } else if (request_dma(dma16, "SoundBlaster - 16bit")) { | ||
266 | snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16); | ||
267 | snd_sbdsp_free(chip); | ||
268 | return -EBUSY; | ||
269 | } | ||
270 | } | ||
271 | chip->dma16 = dma16; | ||
272 | #endif | ||
273 | |||
274 | __skip_allocation: | ||
275 | chip->card = card; | ||
276 | chip->hardware = hardware; | ||
277 | if ((err = snd_sbdsp_probe(chip)) < 0) { | ||
278 | snd_sbdsp_free(chip); | ||
279 | return err; | ||
280 | } | ||
281 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
282 | snd_sbdsp_free(chip); | ||
283 | return err; | ||
284 | } | ||
285 | *r_chip = chip; | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | EXPORT_SYMBOL(snd_sbdsp_command); | ||
290 | EXPORT_SYMBOL(snd_sbdsp_get_byte); | ||
291 | EXPORT_SYMBOL(snd_sbdsp_reset); | ||
292 | EXPORT_SYMBOL(snd_sbdsp_create); | ||
293 | /* sb_mixer.c */ | ||
294 | EXPORT_SYMBOL(snd_sbmixer_write); | ||
295 | EXPORT_SYMBOL(snd_sbmixer_read); | ||
296 | EXPORT_SYMBOL(snd_sbmixer_new); | ||
297 | EXPORT_SYMBOL(snd_sbmixer_add_ctl); | ||
298 | |||
299 | /* | ||
300 | * INIT part | ||
301 | */ | ||
302 | |||
303 | static int __init alsa_sb_common_init(void) | ||
304 | { | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static void __exit alsa_sb_common_exit(void) | ||
309 | { | ||
310 | } | ||
311 | |||
312 | module_init(alsa_sb_common_init) | ||
313 | module_exit(alsa_sb_common_exit) | ||
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c new file mode 100644 index 000000000000..cc5a2c6dec16 --- /dev/null +++ b/sound/isa/sb/sb_mixer.c | |||
@@ -0,0 +1,844 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for Sound Blaster mixer control | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/sb.h> | ||
28 | #include <sound/control.h> | ||
29 | |||
30 | #undef IO_DEBUG | ||
31 | |||
32 | void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data) | ||
33 | { | ||
34 | outb(reg, SBP(chip, MIXER_ADDR)); | ||
35 | udelay(10); | ||
36 | outb(data, SBP(chip, MIXER_DATA)); | ||
37 | udelay(10); | ||
38 | #ifdef IO_DEBUG | ||
39 | snd_printk("mixer_write 0x%x 0x%x\n", reg, data); | ||
40 | #endif | ||
41 | } | ||
42 | |||
43 | unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) | ||
44 | { | ||
45 | unsigned char result; | ||
46 | |||
47 | outb(reg, SBP(chip, MIXER_ADDR)); | ||
48 | udelay(10); | ||
49 | result = inb(SBP(chip, MIXER_DATA)); | ||
50 | udelay(10); | ||
51 | #ifdef IO_DEBUG | ||
52 | snd_printk("mixer_read 0x%x 0x%x\n", reg, result); | ||
53 | #endif | ||
54 | return result; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * Single channel mixer element | ||
59 | */ | ||
60 | |||
61 | static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
62 | { | ||
63 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
64 | |||
65 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
66 | uinfo->count = 1; | ||
67 | uinfo->value.integer.min = 0; | ||
68 | uinfo->value.integer.max = mask; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int snd_sbmixer_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
73 | { | ||
74 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
75 | unsigned long flags; | ||
76 | int reg = kcontrol->private_value & 0xff; | ||
77 | int shift = (kcontrol->private_value >> 16) & 0xff; | ||
78 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
79 | unsigned char val; | ||
80 | |||
81 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
82 | val = (snd_sbmixer_read(sb, reg) >> shift) & mask; | ||
83 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
84 | ucontrol->value.integer.value[0] = val; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
89 | { | ||
90 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
91 | unsigned long flags; | ||
92 | int reg = kcontrol->private_value & 0xff; | ||
93 | int shift = (kcontrol->private_value >> 16) & 0x07; | ||
94 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
95 | int change; | ||
96 | unsigned char val, oval; | ||
97 | |||
98 | val = (ucontrol->value.integer.value[0] & mask) << shift; | ||
99 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
100 | oval = snd_sbmixer_read(sb, reg); | ||
101 | val = (oval & ~(mask << shift)) | val; | ||
102 | change = val != oval; | ||
103 | if (change) | ||
104 | snd_sbmixer_write(sb, reg, val); | ||
105 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
106 | return change; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * Double channel mixer element | ||
111 | */ | ||
112 | |||
113 | static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
114 | { | ||
115 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
116 | |||
117 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
118 | uinfo->count = 2; | ||
119 | uinfo->value.integer.min = 0; | ||
120 | uinfo->value.integer.max = mask; | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int snd_sbmixer_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
125 | { | ||
126 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
127 | unsigned long flags; | ||
128 | int left_reg = kcontrol->private_value & 0xff; | ||
129 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
130 | int left_shift = (kcontrol->private_value >> 16) & 0x07; | ||
131 | int right_shift = (kcontrol->private_value >> 19) & 0x07; | ||
132 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
133 | unsigned char left, right; | ||
134 | |||
135 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
136 | left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask; | ||
137 | right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask; | ||
138 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
139 | ucontrol->value.integer.value[0] = left; | ||
140 | ucontrol->value.integer.value[1] = right; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int snd_sbmixer_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
145 | { | ||
146 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
147 | unsigned long flags; | ||
148 | int left_reg = kcontrol->private_value & 0xff; | ||
149 | int right_reg = (kcontrol->private_value >> 8) & 0xff; | ||
150 | int left_shift = (kcontrol->private_value >> 16) & 0x07; | ||
151 | int right_shift = (kcontrol->private_value >> 19) & 0x07; | ||
152 | int mask = (kcontrol->private_value >> 24) & 0xff; | ||
153 | int change; | ||
154 | unsigned char left, right, oleft, oright; | ||
155 | |||
156 | left = (ucontrol->value.integer.value[0] & mask) << left_shift; | ||
157 | right = (ucontrol->value.integer.value[1] & mask) << right_shift; | ||
158 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
159 | if (left_reg == right_reg) { | ||
160 | oleft = snd_sbmixer_read(sb, left_reg); | ||
161 | left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right; | ||
162 | change = left != oleft; | ||
163 | if (change) | ||
164 | snd_sbmixer_write(sb, left_reg, left); | ||
165 | } else { | ||
166 | oleft = snd_sbmixer_read(sb, left_reg); | ||
167 | oright = snd_sbmixer_read(sb, right_reg); | ||
168 | left = (oleft & ~(mask << left_shift)) | left; | ||
169 | right = (oright & ~(mask << right_shift)) | right; | ||
170 | change = left != oleft || right != oright; | ||
171 | if (change) { | ||
172 | snd_sbmixer_write(sb, left_reg, left); | ||
173 | snd_sbmixer_write(sb, right_reg, right); | ||
174 | } | ||
175 | } | ||
176 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
177 | return change; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * DT-019x / ALS-007 capture/input switch | ||
182 | */ | ||
183 | |||
184 | static int snd_dt019x_input_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
185 | { | ||
186 | static char *texts[5] = { | ||
187 | "CD", "Mic", "Line", "Synth", "Master" | ||
188 | }; | ||
189 | |||
190 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
191 | uinfo->count = 1; | ||
192 | uinfo->value.enumerated.items = 5; | ||
193 | if (uinfo->value.enumerated.item > 4) | ||
194 | uinfo->value.enumerated.item = 4; | ||
195 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static int snd_dt019x_input_sw_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
200 | { | ||
201 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
202 | unsigned long flags; | ||
203 | unsigned char oval; | ||
204 | |||
205 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
206 | oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW); | ||
207 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
208 | switch (oval & 0x07) { | ||
209 | case SB_DT019X_CAP_CD: | ||
210 | ucontrol->value.enumerated.item[0] = 0; | ||
211 | break; | ||
212 | case SB_DT019X_CAP_MIC: | ||
213 | ucontrol->value.enumerated.item[0] = 1; | ||
214 | break; | ||
215 | case SB_DT019X_CAP_LINE: | ||
216 | ucontrol->value.enumerated.item[0] = 2; | ||
217 | break; | ||
218 | case SB_DT019X_CAP_MAIN: | ||
219 | ucontrol->value.enumerated.item[0] = 4; | ||
220 | break; | ||
221 | /* To record the synth on these cards you must record the main. */ | ||
222 | /* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */ | ||
223 | /* duplicate case labels if left uncommented. */ | ||
224 | /* case SB_DT019X_CAP_SYNTH: | ||
225 | * ucontrol->value.enumerated.item[0] = 3; | ||
226 | * break; | ||
227 | */ | ||
228 | default: | ||
229 | ucontrol->value.enumerated.item[0] = 4; | ||
230 | break; | ||
231 | } | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int snd_dt019x_input_sw_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
236 | { | ||
237 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
238 | unsigned long flags; | ||
239 | int change; | ||
240 | unsigned char nval, oval; | ||
241 | |||
242 | if (ucontrol->value.enumerated.item[0] > 4) | ||
243 | return -EINVAL; | ||
244 | switch (ucontrol->value.enumerated.item[0]) { | ||
245 | case 0: | ||
246 | nval = SB_DT019X_CAP_CD; | ||
247 | break; | ||
248 | case 1: | ||
249 | nval = SB_DT019X_CAP_MIC; | ||
250 | break; | ||
251 | case 2: | ||
252 | nval = SB_DT019X_CAP_LINE; | ||
253 | break; | ||
254 | case 3: | ||
255 | nval = SB_DT019X_CAP_SYNTH; | ||
256 | break; | ||
257 | case 4: | ||
258 | nval = SB_DT019X_CAP_MAIN; | ||
259 | break; | ||
260 | default: | ||
261 | nval = SB_DT019X_CAP_MAIN; | ||
262 | } | ||
263 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
264 | oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW); | ||
265 | change = nval != oval; | ||
266 | if (change) | ||
267 | snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval); | ||
268 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
269 | return change; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * SBPRO input multiplexer | ||
274 | */ | ||
275 | |||
276 | static int snd_sb8mixer_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
277 | { | ||
278 | static char *texts[3] = { | ||
279 | "Mic", "CD", "Line" | ||
280 | }; | ||
281 | |||
282 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
283 | uinfo->count = 1; | ||
284 | uinfo->value.enumerated.items = 3; | ||
285 | if (uinfo->value.enumerated.item > 2) | ||
286 | uinfo->value.enumerated.item = 2; | ||
287 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | |||
292 | static int snd_sb8mixer_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
293 | { | ||
294 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
295 | unsigned long flags; | ||
296 | unsigned char oval; | ||
297 | |||
298 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
299 | oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); | ||
300 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
301 | switch ((oval >> 0x01) & 0x03) { | ||
302 | case SB_DSP_MIXS_CD: | ||
303 | ucontrol->value.enumerated.item[0] = 1; | ||
304 | break; | ||
305 | case SB_DSP_MIXS_LINE: | ||
306 | ucontrol->value.enumerated.item[0] = 2; | ||
307 | break; | ||
308 | default: | ||
309 | ucontrol->value.enumerated.item[0] = 0; | ||
310 | break; | ||
311 | } | ||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
316 | { | ||
317 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
318 | unsigned long flags; | ||
319 | int change; | ||
320 | unsigned char nval, oval; | ||
321 | |||
322 | if (ucontrol->value.enumerated.item[0] > 2) | ||
323 | return -EINVAL; | ||
324 | switch (ucontrol->value.enumerated.item[0]) { | ||
325 | case 1: | ||
326 | nval = SB_DSP_MIXS_CD; | ||
327 | break; | ||
328 | case 2: | ||
329 | nval = SB_DSP_MIXS_LINE; | ||
330 | break; | ||
331 | default: | ||
332 | nval = SB_DSP_MIXS_MIC; | ||
333 | } | ||
334 | nval <<= 1; | ||
335 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
336 | oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); | ||
337 | nval |= oval & ~0x06; | ||
338 | change = nval != oval; | ||
339 | if (change) | ||
340 | snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval); | ||
341 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
342 | return change; | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * SB16 input switch | ||
347 | */ | ||
348 | |||
349 | static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) | ||
350 | { | ||
351 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
352 | uinfo->count = 4; | ||
353 | uinfo->value.integer.min = 0; | ||
354 | uinfo->value.integer.max = 1; | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static int snd_sb16mixer_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
359 | { | ||
360 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
361 | unsigned long flags; | ||
362 | int reg1 = kcontrol->private_value & 0xff; | ||
363 | int reg2 = (kcontrol->private_value >> 8) & 0xff; | ||
364 | int left_shift = (kcontrol->private_value >> 16) & 0x0f; | ||
365 | int right_shift = (kcontrol->private_value >> 24) & 0x0f; | ||
366 | unsigned char val1, val2; | ||
367 | |||
368 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
369 | val1 = snd_sbmixer_read(sb, reg1); | ||
370 | val2 = snd_sbmixer_read(sb, reg2); | ||
371 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
372 | ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01; | ||
373 | ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01; | ||
374 | ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01; | ||
375 | ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01; | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
380 | { | ||
381 | sb_t *sb = snd_kcontrol_chip(kcontrol); | ||
382 | unsigned long flags; | ||
383 | int reg1 = kcontrol->private_value & 0xff; | ||
384 | int reg2 = (kcontrol->private_value >> 8) & 0xff; | ||
385 | int left_shift = (kcontrol->private_value >> 16) & 0x0f; | ||
386 | int right_shift = (kcontrol->private_value >> 24) & 0x0f; | ||
387 | int change; | ||
388 | unsigned char val1, val2, oval1, oval2; | ||
389 | |||
390 | spin_lock_irqsave(&sb->mixer_lock, flags); | ||
391 | oval1 = snd_sbmixer_read(sb, reg1); | ||
392 | oval2 = snd_sbmixer_read(sb, reg2); | ||
393 | val1 = oval1 & ~((1 << left_shift) | (1 << right_shift)); | ||
394 | val2 = oval2 & ~((1 << left_shift) | (1 << right_shift)); | ||
395 | val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; | ||
396 | val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; | ||
397 | val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; | ||
398 | val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; | ||
399 | change = val1 != oval1 || val2 != oval2; | ||
400 | if (change) { | ||
401 | snd_sbmixer_write(sb, reg1, val1); | ||
402 | snd_sbmixer_write(sb, reg2, val2); | ||
403 | } | ||
404 | spin_unlock_irqrestore(&sb->mixer_lock, flags); | ||
405 | return change; | ||
406 | } | ||
407 | |||
408 | |||
409 | /* | ||
410 | */ | ||
411 | /* | ||
412 | */ | ||
413 | int snd_sbmixer_add_ctl(sb_t *chip, const char *name, int index, int type, unsigned long value) | ||
414 | { | ||
415 | static snd_kcontrol_new_t newctls[] = { | ||
416 | [SB_MIX_SINGLE] = { | ||
417 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
418 | .info = snd_sbmixer_info_single, | ||
419 | .get = snd_sbmixer_get_single, | ||
420 | .put = snd_sbmixer_put_single, | ||
421 | }, | ||
422 | [SB_MIX_DOUBLE] = { | ||
423 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
424 | .info = snd_sbmixer_info_double, | ||
425 | .get = snd_sbmixer_get_double, | ||
426 | .put = snd_sbmixer_put_double, | ||
427 | }, | ||
428 | [SB_MIX_INPUT_SW] = { | ||
429 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
430 | .info = snd_sb16mixer_info_input_sw, | ||
431 | .get = snd_sb16mixer_get_input_sw, | ||
432 | .put = snd_sb16mixer_put_input_sw, | ||
433 | }, | ||
434 | [SB_MIX_CAPTURE_PRO] = { | ||
435 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
436 | .info = snd_sb8mixer_info_mux, | ||
437 | .get = snd_sb8mixer_get_mux, | ||
438 | .put = snd_sb8mixer_put_mux, | ||
439 | }, | ||
440 | [SB_MIX_CAPTURE_DT019X] = { | ||
441 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
442 | .info = snd_dt019x_input_sw_info, | ||
443 | .get = snd_dt019x_input_sw_get, | ||
444 | .put = snd_dt019x_input_sw_put, | ||
445 | }, | ||
446 | }; | ||
447 | snd_kcontrol_t *ctl; | ||
448 | int err; | ||
449 | |||
450 | ctl = snd_ctl_new1(&newctls[type], chip); | ||
451 | if (! ctl) | ||
452 | return -ENOMEM; | ||
453 | strlcpy(ctl->id.name, name, sizeof(ctl->id.name)); | ||
454 | ctl->id.index = index; | ||
455 | ctl->private_value = value; | ||
456 | if ((err = snd_ctl_add(chip->card, ctl)) < 0) { | ||
457 | snd_ctl_free_one(ctl); | ||
458 | return err; | ||
459 | } | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * SB 2.0 specific mixer elements | ||
465 | */ | ||
466 | |||
467 | static struct sbmix_elem snd_sb20_ctl_master_play_vol = | ||
468 | SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7); | ||
469 | static struct sbmix_elem snd_sb20_ctl_pcm_play_vol = | ||
470 | SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3); | ||
471 | static struct sbmix_elem snd_sb20_ctl_synth_play_vol = | ||
472 | SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7); | ||
473 | static struct sbmix_elem snd_sb20_ctl_cd_play_vol = | ||
474 | SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7); | ||
475 | |||
476 | static struct sbmix_elem *snd_sb20_controls[] = { | ||
477 | &snd_sb20_ctl_master_play_vol, | ||
478 | &snd_sb20_ctl_pcm_play_vol, | ||
479 | &snd_sb20_ctl_synth_play_vol, | ||
480 | &snd_sb20_ctl_cd_play_vol | ||
481 | }; | ||
482 | |||
483 | static unsigned char snd_sb20_init_values[][2] = { | ||
484 | { SB_DSP20_MASTER_DEV, 0 }, | ||
485 | { SB_DSP20_FM_DEV, 0 }, | ||
486 | }; | ||
487 | |||
488 | /* | ||
489 | * SB Pro specific mixer elements | ||
490 | */ | ||
491 | static struct sbmix_elem snd_sbpro_ctl_master_play_vol = | ||
492 | SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7); | ||
493 | static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol = | ||
494 | SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7); | ||
495 | static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter = | ||
496 | SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1); | ||
497 | static struct sbmix_elem snd_sbpro_ctl_synth_play_vol = | ||
498 | SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7); | ||
499 | static struct sbmix_elem snd_sbpro_ctl_cd_play_vol = | ||
500 | SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7); | ||
501 | static struct sbmix_elem snd_sbpro_ctl_line_play_vol = | ||
502 | SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7); | ||
503 | static struct sbmix_elem snd_sbpro_ctl_mic_play_vol = | ||
504 | SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3); | ||
505 | static struct sbmix_elem snd_sbpro_ctl_capture_source = | ||
506 | { | ||
507 | .name = "Capture Source", | ||
508 | .type = SB_MIX_CAPTURE_PRO | ||
509 | }; | ||
510 | static struct sbmix_elem snd_sbpro_ctl_capture_filter = | ||
511 | SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1); | ||
512 | static struct sbmix_elem snd_sbpro_ctl_capture_low_filter = | ||
513 | SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1); | ||
514 | |||
515 | static struct sbmix_elem *snd_sbpro_controls[] = { | ||
516 | &snd_sbpro_ctl_master_play_vol, | ||
517 | &snd_sbpro_ctl_pcm_play_vol, | ||
518 | &snd_sbpro_ctl_pcm_play_filter, | ||
519 | &snd_sbpro_ctl_synth_play_vol, | ||
520 | &snd_sbpro_ctl_cd_play_vol, | ||
521 | &snd_sbpro_ctl_line_play_vol, | ||
522 | &snd_sbpro_ctl_mic_play_vol, | ||
523 | &snd_sbpro_ctl_capture_source, | ||
524 | &snd_sbpro_ctl_capture_filter, | ||
525 | &snd_sbpro_ctl_capture_low_filter | ||
526 | }; | ||
527 | |||
528 | static unsigned char snd_sbpro_init_values[][2] = { | ||
529 | { SB_DSP_MASTER_DEV, 0 }, | ||
530 | { SB_DSP_PCM_DEV, 0 }, | ||
531 | { SB_DSP_FM_DEV, 0 }, | ||
532 | }; | ||
533 | |||
534 | /* | ||
535 | * SB16 specific mixer elements | ||
536 | */ | ||
537 | static struct sbmix_elem snd_sb16_ctl_master_play_vol = | ||
538 | SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31); | ||
539 | static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch = | ||
540 | SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1); | ||
541 | static struct sbmix_elem snd_sb16_ctl_tone_bass = | ||
542 | SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15); | ||
543 | static struct sbmix_elem snd_sb16_ctl_tone_treble = | ||
544 | SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15); | ||
545 | static struct sbmix_elem snd_sb16_ctl_pcm_play_vol = | ||
546 | SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31); | ||
547 | static struct sbmix_elem snd_sb16_ctl_synth_capture_route = | ||
548 | SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5); | ||
549 | static struct sbmix_elem snd_sb16_ctl_synth_play_vol = | ||
550 | SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31); | ||
551 | static struct sbmix_elem snd_sb16_ctl_cd_capture_route = | ||
552 | SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1); | ||
553 | static struct sbmix_elem snd_sb16_ctl_cd_play_switch = | ||
554 | SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1); | ||
555 | static struct sbmix_elem snd_sb16_ctl_cd_play_vol = | ||
556 | SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31); | ||
557 | static struct sbmix_elem snd_sb16_ctl_line_capture_route = | ||
558 | SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3); | ||
559 | static struct sbmix_elem snd_sb16_ctl_line_play_switch = | ||
560 | SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1); | ||
561 | static struct sbmix_elem snd_sb16_ctl_line_play_vol = | ||
562 | SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31); | ||
563 | static struct sbmix_elem snd_sb16_ctl_mic_capture_route = | ||
564 | SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0); | ||
565 | static struct sbmix_elem snd_sb16_ctl_mic_play_switch = | ||
566 | SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1); | ||
567 | static struct sbmix_elem snd_sb16_ctl_mic_play_vol = | ||
568 | SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31); | ||
569 | static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol = | ||
570 | SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3); | ||
571 | static struct sbmix_elem snd_sb16_ctl_capture_vol = | ||
572 | SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3); | ||
573 | static struct sbmix_elem snd_sb16_ctl_play_vol = | ||
574 | SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3); | ||
575 | static struct sbmix_elem snd_sb16_ctl_auto_mic_gain = | ||
576 | SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1); | ||
577 | |||
578 | static struct sbmix_elem *snd_sb16_controls[] = { | ||
579 | &snd_sb16_ctl_master_play_vol, | ||
580 | &snd_sb16_ctl_3d_enhance_switch, | ||
581 | &snd_sb16_ctl_tone_bass, | ||
582 | &snd_sb16_ctl_tone_treble, | ||
583 | &snd_sb16_ctl_pcm_play_vol, | ||
584 | &snd_sb16_ctl_synth_capture_route, | ||
585 | &snd_sb16_ctl_synth_play_vol, | ||
586 | &snd_sb16_ctl_cd_capture_route, | ||
587 | &snd_sb16_ctl_cd_play_switch, | ||
588 | &snd_sb16_ctl_cd_play_vol, | ||
589 | &snd_sb16_ctl_line_capture_route, | ||
590 | &snd_sb16_ctl_line_play_switch, | ||
591 | &snd_sb16_ctl_line_play_vol, | ||
592 | &snd_sb16_ctl_mic_capture_route, | ||
593 | &snd_sb16_ctl_mic_play_switch, | ||
594 | &snd_sb16_ctl_mic_play_vol, | ||
595 | &snd_sb16_ctl_pc_speaker_vol, | ||
596 | &snd_sb16_ctl_capture_vol, | ||
597 | &snd_sb16_ctl_play_vol, | ||
598 | &snd_sb16_ctl_auto_mic_gain | ||
599 | }; | ||
600 | |||
601 | static unsigned char snd_sb16_init_values[][2] = { | ||
602 | { SB_DSP4_MASTER_DEV + 0, 0 }, | ||
603 | { SB_DSP4_MASTER_DEV + 1, 0 }, | ||
604 | { SB_DSP4_PCM_DEV + 0, 0 }, | ||
605 | { SB_DSP4_PCM_DEV + 1, 0 }, | ||
606 | { SB_DSP4_SYNTH_DEV + 0, 0 }, | ||
607 | { SB_DSP4_SYNTH_DEV + 1, 0 }, | ||
608 | { SB_DSP4_INPUT_LEFT, 0 }, | ||
609 | { SB_DSP4_INPUT_RIGHT, 0 }, | ||
610 | { SB_DSP4_OUTPUT_SW, 0 }, | ||
611 | { SB_DSP4_SPEAKER_DEV, 0 }, | ||
612 | }; | ||
613 | |||
614 | /* | ||
615 | * DT019x specific mixer elements | ||
616 | */ | ||
617 | static struct sbmix_elem snd_dt019x_ctl_master_play_vol = | ||
618 | SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15); | ||
619 | static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol = | ||
620 | SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15); | ||
621 | static struct sbmix_elem snd_dt019x_ctl_synth_play_vol = | ||
622 | SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15); | ||
623 | static struct sbmix_elem snd_dt019x_ctl_cd_play_vol = | ||
624 | SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15); | ||
625 | static struct sbmix_elem snd_dt019x_ctl_mic_play_vol = | ||
626 | SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7); | ||
627 | static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol = | ||
628 | SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0, 7); | ||
629 | static struct sbmix_elem snd_dt019x_ctl_line_play_vol = | ||
630 | SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15); | ||
631 | static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch = | ||
632 | SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1); | ||
633 | static struct sbmix_elem snd_dt019x_ctl_synth_play_switch = | ||
634 | SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1); | ||
635 | static struct sbmix_elem snd_dt019x_ctl_capture_source = | ||
636 | { | ||
637 | .name = "Capture Source", | ||
638 | .type = SB_MIX_CAPTURE_DT019X | ||
639 | }; | ||
640 | |||
641 | static struct sbmix_elem *snd_dt019x_controls[] = { | ||
642 | &snd_dt019x_ctl_master_play_vol, | ||
643 | &snd_dt019x_ctl_pcm_play_vol, | ||
644 | &snd_dt019x_ctl_synth_play_vol, | ||
645 | &snd_dt019x_ctl_cd_play_vol, | ||
646 | &snd_dt019x_ctl_mic_play_vol, | ||
647 | &snd_dt019x_ctl_pc_speaker_vol, | ||
648 | &snd_dt019x_ctl_line_play_vol, | ||
649 | &snd_sb16_ctl_mic_play_switch, | ||
650 | &snd_sb16_ctl_cd_play_switch, | ||
651 | &snd_sb16_ctl_line_play_switch, | ||
652 | &snd_dt019x_ctl_pcm_play_switch, | ||
653 | &snd_dt019x_ctl_synth_play_switch, | ||
654 | &snd_dt019x_ctl_capture_source | ||
655 | }; | ||
656 | |||
657 | static unsigned char snd_dt019x_init_values[][2] = { | ||
658 | { SB_DT019X_MASTER_DEV, 0 }, | ||
659 | { SB_DT019X_PCM_DEV, 0 }, | ||
660 | { SB_DT019X_SYNTH_DEV, 0 }, | ||
661 | { SB_DT019X_CD_DEV, 0 }, | ||
662 | { SB_DT019X_MIC_DEV, 0 }, /* Includes PC-speaker in high nibble */ | ||
663 | { SB_DT019X_LINE_DEV, 0 }, | ||
664 | { SB_DSP4_OUTPUT_SW, 0 }, | ||
665 | { SB_DT019X_OUTPUT_SW2, 0 }, | ||
666 | { SB_DT019X_CAPTURE_SW, 0x06 }, | ||
667 | }; | ||
668 | |||
669 | /* | ||
670 | * ALS4000 specific mixer elements | ||
671 | */ | ||
672 | /* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl ! */ | ||
673 | static struct sbmix_elem snd_als4000_ctl_mono_output_switch = | ||
674 | SB_SINGLE("Mono Output Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1); | ||
675 | /* FIXME: mono input switch also available on DT019X ? */ | ||
676 | static struct sbmix_elem snd_als4000_ctl_mono_input_switch = | ||
677 | SB_SINGLE("Mono Input Switch", SB_DT019X_OUTPUT_SW2, 0, 1); | ||
678 | static struct sbmix_elem snd_als4000_ctl_mic_20db_boost = | ||
679 | SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03); | ||
680 | static struct sbmix_elem snd_als4000_ctl_mixer_out_to_in = | ||
681 | SB_SINGLE("Mixer Out To In", SB_ALS4000_MIC_IN_GAIN, 7, 0x01); | ||
682 | /* FIXME: 3D needs much more sophisticated controls, many more features ! */ | ||
683 | static struct sbmix_elem snd_als4000_ctl_3d_output_switch = | ||
684 | SB_SINGLE("3D Output Switch", SB_ALS4000_3D_SND_FX, 6, 0x01); | ||
685 | static struct sbmix_elem snd_als4000_ctl_3d_output_ratio = | ||
686 | SB_SINGLE("3D Output Ratio", SB_ALS4000_3D_SND_FX, 0, 0x07); | ||
687 | static struct sbmix_elem snd_als4000_ctl_3d_poweroff_switch = | ||
688 | SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01); | ||
689 | static struct sbmix_elem snd_als4000_ctl_3d_delay = | ||
690 | SB_SINGLE("3D Delay", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f); | ||
691 | #if NOT_AVAILABLE | ||
692 | static struct sbmix_elem snd_als4000_ctl_fmdac = | ||
693 | SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01); | ||
694 | static struct sbmix_elem snd_als4000_ctl_qsound = | ||
695 | SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f); | ||
696 | #endif | ||
697 | |||
698 | static struct sbmix_elem *snd_als4000_controls[] = { | ||
699 | &snd_sb16_ctl_master_play_vol, | ||
700 | &snd_dt019x_ctl_pcm_play_switch, | ||
701 | &snd_sb16_ctl_pcm_play_vol, | ||
702 | &snd_sb16_ctl_synth_capture_route, | ||
703 | &snd_dt019x_ctl_synth_play_switch, | ||
704 | &snd_sb16_ctl_synth_play_vol, | ||
705 | &snd_sb16_ctl_cd_capture_route, | ||
706 | &snd_sb16_ctl_cd_play_switch, | ||
707 | &snd_sb16_ctl_cd_play_vol, | ||
708 | &snd_sb16_ctl_line_capture_route, | ||
709 | &snd_sb16_ctl_line_play_switch, | ||
710 | &snd_sb16_ctl_line_play_vol, | ||
711 | &snd_sb16_ctl_mic_capture_route, | ||
712 | &snd_als4000_ctl_mic_20db_boost, | ||
713 | &snd_sb16_ctl_auto_mic_gain, | ||
714 | &snd_sb16_ctl_mic_play_switch, | ||
715 | &snd_sb16_ctl_mic_play_vol, | ||
716 | &snd_sb16_ctl_pc_speaker_vol, | ||
717 | &snd_sb16_ctl_capture_vol, | ||
718 | &snd_sb16_ctl_play_vol, | ||
719 | &snd_als4000_ctl_mono_output_switch, | ||
720 | &snd_als4000_ctl_mono_input_switch, | ||
721 | &snd_als4000_ctl_mixer_out_to_in, | ||
722 | &snd_als4000_ctl_3d_output_switch, | ||
723 | &snd_als4000_ctl_3d_output_ratio, | ||
724 | &snd_als4000_ctl_3d_delay, | ||
725 | &snd_als4000_ctl_3d_poweroff_switch, | ||
726 | #if NOT_AVAILABLE | ||
727 | &snd_als4000_ctl_fmdac, | ||
728 | &snd_als4000_ctl_qsound, | ||
729 | #endif | ||
730 | }; | ||
731 | |||
732 | static unsigned char snd_als4000_init_values[][2] = { | ||
733 | { SB_DSP4_MASTER_DEV + 0, 0 }, | ||
734 | { SB_DSP4_MASTER_DEV + 1, 0 }, | ||
735 | { SB_DSP4_PCM_DEV + 0, 0 }, | ||
736 | { SB_DSP4_PCM_DEV + 1, 0 }, | ||
737 | { SB_DSP4_SYNTH_DEV + 0, 0 }, | ||
738 | { SB_DSP4_SYNTH_DEV + 1, 0 }, | ||
739 | { SB_DSP4_SPEAKER_DEV, 0 }, | ||
740 | { SB_DSP4_OUTPUT_SW, 0 }, | ||
741 | { SB_DSP4_INPUT_LEFT, 0 }, | ||
742 | { SB_DSP4_INPUT_RIGHT, 0 }, | ||
743 | { SB_DT019X_OUTPUT_SW2, 0 }, | ||
744 | { SB_ALS4000_MIC_IN_GAIN, 0 }, | ||
745 | }; | ||
746 | |||
747 | |||
748 | /* | ||
749 | */ | ||
750 | static int snd_sbmixer_init(sb_t *chip, | ||
751 | struct sbmix_elem **controls, | ||
752 | int controls_count, | ||
753 | unsigned char map[][2], | ||
754 | int map_count, | ||
755 | char *name) | ||
756 | { | ||
757 | unsigned long flags; | ||
758 | snd_card_t *card = chip->card; | ||
759 | int idx, err; | ||
760 | |||
761 | /* mixer reset */ | ||
762 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
763 | snd_sbmixer_write(chip, 0x00, 0x00); | ||
764 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
765 | |||
766 | /* mute and zero volume channels */ | ||
767 | for (idx = 0; idx < map_count; idx++) { | ||
768 | spin_lock_irqsave(&chip->mixer_lock, flags); | ||
769 | snd_sbmixer_write(chip, map[idx][0], map[idx][1]); | ||
770 | spin_unlock_irqrestore(&chip->mixer_lock, flags); | ||
771 | } | ||
772 | |||
773 | for (idx = 0; idx < controls_count; idx++) { | ||
774 | if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0) | ||
775 | return err; | ||
776 | } | ||
777 | snd_component_add(card, name); | ||
778 | strcpy(card->mixername, name); | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | int snd_sbmixer_new(sb_t *chip) | ||
783 | { | ||
784 | snd_card_t * card; | ||
785 | int err; | ||
786 | |||
787 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
788 | |||
789 | card = chip->card; | ||
790 | |||
791 | switch (chip->hardware) { | ||
792 | case SB_HW_10: | ||
793 | return 0; /* no mixer chip on SB1.x */ | ||
794 | case SB_HW_20: | ||
795 | case SB_HW_201: | ||
796 | if ((err = snd_sbmixer_init(chip, | ||
797 | snd_sb20_controls, | ||
798 | ARRAY_SIZE(snd_sb20_controls), | ||
799 | snd_sb20_init_values, | ||
800 | ARRAY_SIZE(snd_sb20_init_values), | ||
801 | "CTL1335")) < 0) | ||
802 | return err; | ||
803 | break; | ||
804 | case SB_HW_PRO: | ||
805 | if ((err = snd_sbmixer_init(chip, | ||
806 | snd_sbpro_controls, | ||
807 | ARRAY_SIZE(snd_sbpro_controls), | ||
808 | snd_sbpro_init_values, | ||
809 | ARRAY_SIZE(snd_sbpro_init_values), | ||
810 | "CTL1345")) < 0) | ||
811 | return err; | ||
812 | break; | ||
813 | case SB_HW_16: | ||
814 | case SB_HW_ALS100: | ||
815 | if ((err = snd_sbmixer_init(chip, | ||
816 | snd_sb16_controls, | ||
817 | ARRAY_SIZE(snd_sb16_controls), | ||
818 | snd_sb16_init_values, | ||
819 | ARRAY_SIZE(snd_sb16_init_values), | ||
820 | "CTL1745")) < 0) | ||
821 | return err; | ||
822 | break; | ||
823 | case SB_HW_ALS4000: | ||
824 | if ((err = snd_sbmixer_init(chip, | ||
825 | snd_als4000_controls, | ||
826 | ARRAY_SIZE(snd_als4000_controls), | ||
827 | snd_als4000_init_values, | ||
828 | ARRAY_SIZE(snd_als4000_init_values), | ||
829 | "ALS4000")) < 0) | ||
830 | return err; | ||
831 | break; | ||
832 | case SB_HW_DT019X: | ||
833 | if ((err = snd_sbmixer_init(chip, | ||
834 | snd_dt019x_controls, | ||
835 | ARRAY_SIZE(snd_dt019x_controls), | ||
836 | snd_dt019x_init_values, | ||
837 | ARRAY_SIZE(snd_dt019x_init_values), | ||
838 | "DT019X")) < 0) | ||
839 | break; | ||
840 | default: | ||
841 | strcpy(card->mixername, "???"); | ||
842 | } | ||
843 | return 0; | ||
844 | } | ||
diff --git a/sound/isa/sb/sbawe.c b/sound/isa/sb/sbawe.c new file mode 100644 index 000000000000..2ec52a3473a2 --- /dev/null +++ b/sound/isa/sb/sbawe.c | |||
@@ -0,0 +1,2 @@ | |||
1 | #define SNDRV_SBAWE | ||
2 | #include "sb16.c" | ||
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c new file mode 100644 index 000000000000..17f585b0ddc1 --- /dev/null +++ b/sound/isa/sgalaxy.c | |||
@@ -0,0 +1,322 @@ | |||
1 | /* | ||
2 | * Driver for Aztech Sound Galaxy cards | ||
3 | * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk. | ||
4 | * | ||
5 | * I don't have documentation for this card, I based this driver on the | ||
6 | * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c) | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <asm/dma.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/sb.h> | ||
33 | #include <sound/ad1848.h> | ||
34 | #include <sound/control.h> | ||
35 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
36 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
37 | #include <sound/initval.h> | ||
38 | |||
39 | MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>"); | ||
40 | MODULE_DESCRIPTION("Aztech Sound Galaxy"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}"); | ||
43 | |||
44 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
45 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
46 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
47 | static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ | ||
48 | static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ | ||
49 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ | ||
50 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
51 | |||
52 | module_param_array(index, int, NULL, 0444); | ||
53 | MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); | ||
54 | module_param_array(id, charp, NULL, 0444); | ||
55 | MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); | ||
56 | module_param_array(sbport, long, NULL, 0444); | ||
57 | MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); | ||
58 | module_param_array(wssport, long, NULL, 0444); | ||
59 | MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); | ||
60 | module_param_array(irq, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); | ||
62 | module_param_array(dma1, int, NULL, 0444); | ||
63 | MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); | ||
64 | |||
65 | #define SGALAXY_AUXC_LEFT 18 | ||
66 | #define SGALAXY_AUXC_RIGHT 19 | ||
67 | |||
68 | static snd_card_t *snd_sgalaxy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
69 | |||
70 | /* | ||
71 | |||
72 | */ | ||
73 | |||
74 | #define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) | ||
75 | |||
76 | /* from lowlevel/sb/sb.c - to avoid having to allocate a sb_t for the */ | ||
77 | /* short time we actually need it.. */ | ||
78 | |||
79 | static int snd_sgalaxy_sbdsp_reset(unsigned long port) | ||
80 | { | ||
81 | int i; | ||
82 | |||
83 | outb(1, SBP1(port, RESET)); | ||
84 | udelay(10); | ||
85 | outb(0, SBP1(port, RESET)); | ||
86 | udelay(30); | ||
87 | for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); | ||
88 | if (inb(SBP1(port, READ)) != 0xaa) { | ||
89 | snd_printd("sb_reset: failed at 0x%lx!!!\n", port); | ||
90 | return -ENODEV; | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char val) | ||
96 | { | ||
97 | int i; | ||
98 | |||
99 | for (i = 10000; i; i--) | ||
100 | if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { | ||
101 | outb(val, SBP1(port, COMMAND)); | ||
102 | return 1; | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
109 | { | ||
110 | return IRQ_NONE; | ||
111 | } | ||
112 | |||
113 | static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) | ||
114 | { | ||
115 | static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, | ||
116 | 0x10, 0x18, 0x20, -1, -1, -1, -1}; | ||
117 | static int dma_bits[] = {1, 2, 0, 3}; | ||
118 | int tmp, tmp1; | ||
119 | |||
120 | if ((tmp = inb(port + 3)) == 0xff) | ||
121 | { | ||
122 | snd_printdd("I/O address dead (0x%lx)\n", port); | ||
123 | return 0; | ||
124 | } | ||
125 | #if 0 | ||
126 | snd_printdd("WSS signature = 0x%x\n", tmp); | ||
127 | #endif | ||
128 | |||
129 | if ((tmp & 0x3f) != 0x04 && | ||
130 | (tmp & 0x3f) != 0x0f && | ||
131 | (tmp & 0x3f) != 0x00) { | ||
132 | snd_printdd("No WSS signature detected on port 0x%lx\n", | ||
133 | port + 3); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | #if 0 | ||
138 | snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n"); | ||
139 | #endif | ||
140 | |||
141 | /* initialize IRQ for WSS codec */ | ||
142 | tmp = interrupt_bits[irq % 16]; | ||
143 | if (tmp < 0) | ||
144 | return -EINVAL; | ||
145 | |||
146 | if (request_irq(irq, snd_sgalaxy_dummy_interrupt, SA_INTERRUPT, "sgalaxy", NULL)) { | ||
147 | snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq); | ||
148 | return -EIO; | ||
149 | } | ||
150 | |||
151 | outb(tmp | 0x40, port); | ||
152 | tmp1 = dma_bits[dma % 4]; | ||
153 | outb(tmp | tmp1, port); | ||
154 | |||
155 | free_irq(irq, NULL); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int __init snd_sgalaxy_detect(int dev, int irq, int dma) | ||
161 | { | ||
162 | #if 0 | ||
163 | snd_printdd("sgalaxy - switching to WSS mode\n"); | ||
164 | #endif | ||
165 | |||
166 | /* switch to WSS mode */ | ||
167 | snd_sgalaxy_sbdsp_reset(sbport[dev]); | ||
168 | |||
169 | snd_sgalaxy_sbdsp_command(sbport[dev], 9); | ||
170 | snd_sgalaxy_sbdsp_command(sbport[dev], 0); | ||
171 | |||
172 | udelay(400); | ||
173 | return snd_sgalaxy_setup_wss(wssport[dev], irq, dma); | ||
174 | } | ||
175 | |||
176 | static struct ad1848_mix_elem snd_sgalaxy_controls[] = { | ||
177 | AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), | ||
178 | AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) | ||
179 | }; | ||
180 | |||
181 | static int __init snd_sgalaxy_mixer(ad1848_t *chip) | ||
182 | { | ||
183 | snd_card_t *card = chip->card; | ||
184 | snd_ctl_elem_id_t id1, id2; | ||
185 | unsigned int idx; | ||
186 | int err; | ||
187 | |||
188 | memset(&id1, 0, sizeof(id1)); | ||
189 | memset(&id2, 0, sizeof(id2)); | ||
190 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
191 | /* reassign AUX0 to LINE */ | ||
192 | strcpy(id1.name, "Aux Playback Switch"); | ||
193 | strcpy(id2.name, "Line Playback Switch"); | ||
194 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
195 | return err; | ||
196 | strcpy(id1.name, "Aux Playback Volume"); | ||
197 | strcpy(id2.name, "Line Playback Volume"); | ||
198 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
199 | return err; | ||
200 | /* reassign AUX1 to FM */ | ||
201 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
202 | strcpy(id2.name, "FM Playback Switch"); | ||
203 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
204 | return err; | ||
205 | strcpy(id1.name, "Aux Playback Volume"); | ||
206 | strcpy(id2.name, "FM Playback Volume"); | ||
207 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
208 | return err; | ||
209 | /* build AUX2 input */ | ||
210 | for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { | ||
211 | if ((err = snd_ad1848_add_ctl_elem(chip, &snd_sgalaxy_controls[idx])) < 0) | ||
212 | return err; | ||
213 | } | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int __init snd_sgalaxy_probe(int dev) | ||
218 | { | ||
219 | static int possible_irqs[] = {7, 9, 10, 11, -1}; | ||
220 | static int possible_dmas[] = {1, 3, 0, -1}; | ||
221 | int err, xirq, xdma1; | ||
222 | snd_card_t *card; | ||
223 | ad1848_t *chip; | ||
224 | |||
225 | if (sbport[dev] == SNDRV_AUTO_PORT) { | ||
226 | snd_printk("specify SB port\n"); | ||
227 | return -EINVAL; | ||
228 | } | ||
229 | if (wssport[dev] == SNDRV_AUTO_PORT) { | ||
230 | snd_printk("specify WSS port\n"); | ||
231 | return -EINVAL; | ||
232 | } | ||
233 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
234 | if (card == NULL) | ||
235 | return -ENOMEM; | ||
236 | |||
237 | xirq = irq[dev]; | ||
238 | if (xirq == SNDRV_AUTO_IRQ) { | ||
239 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
240 | snd_card_free(card); | ||
241 | snd_printk("unable to find a free IRQ\n"); | ||
242 | return -EBUSY; | ||
243 | } | ||
244 | } | ||
245 | xdma1 = dma1[dev]; | ||
246 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
247 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
248 | snd_card_free(card); | ||
249 | snd_printk("unable to find a free DMA\n"); | ||
250 | return -EBUSY; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0) { | ||
255 | snd_card_free(card); | ||
256 | return err; | ||
257 | } | ||
258 | |||
259 | if ((err = snd_ad1848_create(card, wssport[dev] + 4, | ||
260 | xirq, xdma1, | ||
261 | AD1848_HW_DETECT, &chip)) < 0) { | ||
262 | snd_card_free(card); | ||
263 | return err; | ||
264 | } | ||
265 | |||
266 | if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) { | ||
267 | snd_printdd("sgalaxy - error creating new ad1848 PCM device\n"); | ||
268 | snd_card_free(card); | ||
269 | return err; | ||
270 | } | ||
271 | if ((err = snd_ad1848_mixer(chip)) < 0) { | ||
272 | snd_printdd("sgalaxy - error creating new ad1848 mixer\n"); | ||
273 | snd_card_free(card); | ||
274 | return err; | ||
275 | } | ||
276 | if (snd_sgalaxy_mixer(chip) < 0) { | ||
277 | snd_printdd("sgalaxy - the mixer rewrite failed\n"); | ||
278 | snd_card_free(card); | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | strcpy(card->driver, "Sound Galaxy"); | ||
283 | strcpy(card->shortname, "Sound Galaxy"); | ||
284 | sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", | ||
285 | wssport[dev], xirq, xdma1); | ||
286 | |||
287 | if ((err = snd_card_register(card)) < 0) { | ||
288 | snd_card_free(card); | ||
289 | return err; | ||
290 | } | ||
291 | snd_sgalaxy_cards[dev] = card; | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int __init alsa_card_sgalaxy_init(void) | ||
296 | { | ||
297 | int dev, cards; | ||
298 | |||
299 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
300 | if (snd_sgalaxy_probe(dev) >= 0) | ||
301 | cards++; | ||
302 | } | ||
303 | if (!cards) { | ||
304 | #ifdef MODULE | ||
305 | snd_printk(KERN_ERR "Sound Galaxy soundcard not found or device busy\n"); | ||
306 | #endif | ||
307 | return -ENODEV; | ||
308 | } | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static void __exit alsa_card_sgalaxy_exit(void) | ||
314 | { | ||
315 | int idx; | ||
316 | |||
317 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
318 | snd_card_free(snd_sgalaxy_cards[idx]); | ||
319 | } | ||
320 | |||
321 | module_init(alsa_card_sgalaxy_init) | ||
322 | module_exit(alsa_card_sgalaxy_exit) | ||
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c new file mode 100644 index 000000000000..3959ed694eec --- /dev/null +++ b/sound/isa/sscape.c | |||
@@ -0,0 +1,1517 @@ | |||
1 | /* | ||
2 | * Low-level ALSA driver for the ENSONIQ SoundScape PnP | ||
3 | * Copyright (c) by Chris Rankin | ||
4 | * | ||
5 | * This driver was written in part using information obtained from | ||
6 | * the OSS/Free SoundScape driver, written by Hannu Savolainen. | ||
7 | * | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/pnp.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/moduleparam.h> | ||
30 | #include <asm/dma.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/hwdep.h> | ||
33 | #include <sound/cs4231.h> | ||
34 | #include <sound/mpu401.h> | ||
35 | #include <sound/initval.h> | ||
36 | |||
37 | #include <sound/sscape_ioctl.h> | ||
38 | |||
39 | |||
40 | MODULE_AUTHOR("Chris Rankin"); | ||
41 | MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver"); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX; | ||
45 | static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR; | ||
46 | static long port[SNDRV_CARDS] __devinitdata = { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_PORT }; | ||
47 | static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ; | ||
48 | static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ; | ||
49 | static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA; | ||
50 | |||
51 | module_param_array(index, int, NULL, 0444); | ||
52 | MODULE_PARM_DESC(index, "Index number for SoundScape soundcard"); | ||
53 | |||
54 | module_param_array(id, charp, NULL, 0444); | ||
55 | MODULE_PARM_DESC(id, "Description for SoundScape card"); | ||
56 | |||
57 | module_param_array(port, long, NULL, 0444); | ||
58 | MODULE_PARM_DESC(port, "Port # for SoundScape driver."); | ||
59 | |||
60 | module_param_array(irq, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver."); | ||
62 | |||
63 | module_param_array(mpu_irq, int, NULL, 0444); | ||
64 | MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver."); | ||
65 | |||
66 | module_param_array(dma, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(dma, "DMA # for SoundScape driver."); | ||
68 | |||
69 | #ifdef CONFIG_PNP | ||
70 | static struct pnp_card_device_id sscape_pnpids[] = { | ||
71 | { .id = "ENS3081", .devs = { { "ENS0000" } } }, | ||
72 | { .id = "" } /* end */ | ||
73 | }; | ||
74 | |||
75 | MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids); | ||
76 | #endif | ||
77 | |||
78 | static snd_card_t *sscape_card[SNDRV_CARDS]; | ||
79 | |||
80 | |||
81 | #define MPU401_IO(i) ((i) + 0) | ||
82 | #define MIDI_DATA_IO(i) ((i) + 0) | ||
83 | #define MIDI_CTRL_IO(i) ((i) + 1) | ||
84 | #define HOST_CTRL_IO(i) ((i) + 2) | ||
85 | #define HOST_DATA_IO(i) ((i) + 3) | ||
86 | #define ODIE_ADDR_IO(i) ((i) + 4) | ||
87 | #define ODIE_DATA_IO(i) ((i) + 5) | ||
88 | #define CODEC_IO(i) ((i) + 8) | ||
89 | |||
90 | #define IC_ODIE 1 | ||
91 | #define IC_OPUS 2 | ||
92 | |||
93 | #define RX_READY 0x01 | ||
94 | #define TX_READY 0x02 | ||
95 | |||
96 | #define CMD_ACK 0x80 | ||
97 | #define CMD_SET_MIDI_VOL 0x84 | ||
98 | #define CMD_GET_MIDI_VOL 0x85 | ||
99 | #define CMD_XXX_MIDI_VOL 0x86 | ||
100 | #define CMD_SET_EXTMIDI 0x8a | ||
101 | #define CMD_GET_EXTMIDI 0x8b | ||
102 | #define CMD_SET_MT32 0x8c | ||
103 | #define CMD_GET_MT32 0x8d | ||
104 | |||
105 | enum GA_REG { | ||
106 | GA_INTSTAT_REG = 0, | ||
107 | GA_INTENA_REG, | ||
108 | GA_DMAA_REG, | ||
109 | GA_DMAB_REG, | ||
110 | GA_INTCFG_REG, | ||
111 | GA_DMACFG_REG, | ||
112 | GA_CDCFG_REG, | ||
113 | GA_SMCFGA_REG, | ||
114 | GA_SMCFGB_REG, | ||
115 | GA_HMCTL_REG | ||
116 | }; | ||
117 | |||
118 | #define DMA_8BIT 0x80 | ||
119 | |||
120 | |||
121 | #define AD1845_FREQ_SEL_MSB 0x16 | ||
122 | #define AD1845_FREQ_SEL_LSB 0x17 | ||
123 | |||
124 | struct soundscape { | ||
125 | spinlock_t lock; | ||
126 | unsigned io_base; | ||
127 | int codec_type; | ||
128 | int ic_type; | ||
129 | struct resource *io_res; | ||
130 | cs4231_t *chip; | ||
131 | mpu401_t *mpu; | ||
132 | snd_hwdep_t *hw; | ||
133 | |||
134 | /* | ||
135 | * The MIDI device won't work until we've loaded | ||
136 | * its firmware via a hardware-dependent device IOCTL | ||
137 | */ | ||
138 | spinlock_t fwlock; | ||
139 | int hw_in_use; | ||
140 | unsigned long midi_usage; | ||
141 | unsigned char midi_vol; | ||
142 | }; | ||
143 | |||
144 | #define INVALID_IRQ ((unsigned)-1) | ||
145 | |||
146 | |||
147 | static inline struct soundscape *get_card_soundscape(snd_card_t * c) | ||
148 | { | ||
149 | return (struct soundscape *) (c->private_data); | ||
150 | } | ||
151 | |||
152 | static inline struct soundscape *get_mpu401_soundscape(mpu401_t * mpu) | ||
153 | { | ||
154 | return (struct soundscape *) (mpu->private_data); | ||
155 | } | ||
156 | |||
157 | static inline struct soundscape *get_hwdep_soundscape(snd_hwdep_t * hw) | ||
158 | { | ||
159 | return (struct soundscape *) (hw->private_data); | ||
160 | } | ||
161 | |||
162 | |||
163 | /* | ||
164 | * Allocates some kernel memory that we can use for DMA. | ||
165 | * I think this means that the memory has to map to | ||
166 | * contiguous pages of physical memory. | ||
167 | */ | ||
168 | static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size) | ||
169 | { | ||
170 | if (buf) { | ||
171 | if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), | ||
172 | size, buf) < 0) { | ||
173 | snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size); | ||
174 | return NULL; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | return buf; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * Release the DMA-able kernel memory ... | ||
183 | */ | ||
184 | static void free_dmabuf(struct snd_dma_buffer *buf) | ||
185 | { | ||
186 | if (buf && buf->area) | ||
187 | snd_dma_free_pages(buf); | ||
188 | } | ||
189 | |||
190 | |||
191 | /* | ||
192 | * This function writes to the SoundScape's control registers, | ||
193 | * but doesn't do any locking. It's up to the caller to do that. | ||
194 | * This is why this function is "unsafe" ... | ||
195 | */ | ||
196 | static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val) | ||
197 | { | ||
198 | outb(reg, ODIE_ADDR_IO(io_base)); | ||
199 | outb(val, ODIE_DATA_IO(io_base)); | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * Write to the SoundScape's control registers, and do the | ||
204 | * necessary locking ... | ||
205 | */ | ||
206 | static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val) | ||
207 | { | ||
208 | unsigned long flags; | ||
209 | |||
210 | spin_lock_irqsave(&s->lock, flags); | ||
211 | sscape_write_unsafe(s->io_base, reg, val); | ||
212 | spin_unlock_irqrestore(&s->lock, flags); | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * Read from the SoundScape's control registers, but leave any | ||
217 | * locking to the caller. This is why the function is "unsafe" ... | ||
218 | */ | ||
219 | static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg) | ||
220 | { | ||
221 | outb(reg, ODIE_ADDR_IO(io_base)); | ||
222 | return inb(ODIE_DATA_IO(io_base)); | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * Puts the SoundScape into "host" mode, as compared to "MIDI" mode | ||
227 | */ | ||
228 | static inline void set_host_mode_unsafe(unsigned io_base) | ||
229 | { | ||
230 | outb(0x0, HOST_CTRL_IO(io_base)); | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * Puts the SoundScape into "MIDI" mode, as compared to "host" mode | ||
235 | */ | ||
236 | static inline void set_midi_mode_unsafe(unsigned io_base) | ||
237 | { | ||
238 | outb(0x3, HOST_CTRL_IO(io_base)); | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Read the SoundScape's host-mode control register, but leave | ||
243 | * any locking issues to the caller ... | ||
244 | */ | ||
245 | static inline int host_read_unsafe(unsigned io_base) | ||
246 | { | ||
247 | int data = -1; | ||
248 | if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) { | ||
249 | data = inb(HOST_DATA_IO(io_base)); | ||
250 | } | ||
251 | |||
252 | return data; | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * Read the SoundScape's host-mode control register, performing | ||
257 | * a limited amount of busy-waiting if the register isn't ready. | ||
258 | * Also leaves all locking-issues to the caller ... | ||
259 | */ | ||
260 | static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout) | ||
261 | { | ||
262 | int data; | ||
263 | |||
264 | while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) { | ||
265 | udelay(100); | ||
266 | --timeout; | ||
267 | } /* while */ | ||
268 | |||
269 | return data; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * Write to the SoundScape's host-mode control registers, but | ||
274 | * leave any locking issues to the caller ... | ||
275 | */ | ||
276 | static inline int host_write_unsafe(unsigned io_base, unsigned char data) | ||
277 | { | ||
278 | if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) { | ||
279 | outb(data, HOST_DATA_IO(io_base)); | ||
280 | return 1; | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * Write to the SoundScape's host-mode control registers, performing | ||
288 | * a limited amount of busy-waiting if the register isn't ready. | ||
289 | * Also leaves all locking-issues to the caller ... | ||
290 | */ | ||
291 | static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data, | ||
292 | unsigned timeout) | ||
293 | { | ||
294 | int err; | ||
295 | |||
296 | while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) { | ||
297 | udelay(100); | ||
298 | --timeout; | ||
299 | } /* while */ | ||
300 | |||
301 | return err; | ||
302 | } | ||
303 | |||
304 | |||
305 | /* | ||
306 | * Check that the MIDI subsystem is operational. If it isn't, | ||
307 | * then we will hang the computer if we try to use it ... | ||
308 | * | ||
309 | * NOTE: This check is based upon observation, not documentation. | ||
310 | */ | ||
311 | static inline int verify_mpu401(const mpu401_t * mpu) | ||
312 | { | ||
313 | return ((inb(MIDI_CTRL_IO(mpu->port)) & 0xc0) == 0x80); | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * This is apparently the standard way to initailise an MPU-401 | ||
318 | */ | ||
319 | static inline void initialise_mpu401(const mpu401_t * mpu) | ||
320 | { | ||
321 | outb(0, MIDI_DATA_IO(mpu->port)); | ||
322 | } | ||
323 | |||
324 | /* | ||
325 | * Tell the SoundScape to activate the AD1845 chip (I think). | ||
326 | * The AD1845 detection fails if we *don't* do this, so I | ||
327 | * think that this is a good idea ... | ||
328 | */ | ||
329 | static inline void activate_ad1845_unsafe(unsigned io_base) | ||
330 | { | ||
331 | sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10); | ||
332 | sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80); | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Do the necessary ALSA-level cleanup to deallocate our driver ... | ||
337 | */ | ||
338 | static void soundscape_free(snd_card_t * c) | ||
339 | { | ||
340 | register struct soundscape *sscape = get_card_soundscape(c); | ||
341 | release_resource(sscape->io_res); | ||
342 | kfree_nocheck(sscape->io_res); | ||
343 | free_dma(sscape->chip->dma1); | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Put this process into an idle wait-state for a certain number | ||
348 | * of "jiffies". The process can almost certainly be rescheduled | ||
349 | * while we're waiting, and so we must NOT be holding any spinlocks | ||
350 | * when we call this function. If we are then we risk DEADLOCK in | ||
351 | * SMP (Ha!) or pre-emptible kernels. | ||
352 | */ | ||
353 | static inline void sleep(long jiffs, int state) | ||
354 | { | ||
355 | set_current_state(state); | ||
356 | schedule_timeout(jiffs); | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Tell the SoundScape to begin a DMA tranfer using the given channel. | ||
361 | * All locking issues are left to the caller. | ||
362 | */ | ||
363 | static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg) | ||
364 | { | ||
365 | sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01); | ||
366 | sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe); | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * Wait for a DMA transfer to complete. This is a "limited busy-wait", | ||
371 | * and all locking issues are left to the caller. | ||
372 | */ | ||
373 | static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout) | ||
374 | { | ||
375 | while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) { | ||
376 | udelay(100); | ||
377 | --timeout; | ||
378 | } /* while */ | ||
379 | |||
380 | return (sscape_read_unsafe(io_base, reg) & 0x01); | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * Wait for the On-Board Processor to return its start-up | ||
385 | * acknowledgement sequence. This wait is too long for | ||
386 | * us to perform "busy-waiting", and so we must sleep. | ||
387 | * This in turn means that we must not be holding any | ||
388 | * spinlocks when we call this function. | ||
389 | */ | ||
390 | static int obp_startup_ack(struct soundscape *s, unsigned timeout) | ||
391 | { | ||
392 | while (timeout != 0) { | ||
393 | unsigned long flags; | ||
394 | unsigned char x; | ||
395 | |||
396 | sleep(1, TASK_INTERRUPTIBLE); | ||
397 | |||
398 | spin_lock_irqsave(&s->lock, flags); | ||
399 | x = inb(HOST_DATA_IO(s->io_base)); | ||
400 | spin_unlock_irqrestore(&s->lock, flags); | ||
401 | if ((x & 0xfe) == 0xfe) | ||
402 | return 1; | ||
403 | |||
404 | --timeout; | ||
405 | } /* while */ | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | /* | ||
411 | * Wait for the host to return its start-up acknowledgement | ||
412 | * sequence. This wait is too long for us to perform | ||
413 | * "busy-waiting", and so we must sleep. This in turn means | ||
414 | * that we must not be holding any spinlocks when we call | ||
415 | * this function. | ||
416 | */ | ||
417 | static int host_startup_ack(struct soundscape *s, unsigned timeout) | ||
418 | { | ||
419 | while (timeout != 0) { | ||
420 | unsigned long flags; | ||
421 | unsigned char x; | ||
422 | |||
423 | sleep(1, TASK_INTERRUPTIBLE); | ||
424 | |||
425 | spin_lock_irqsave(&s->lock, flags); | ||
426 | x = inb(HOST_DATA_IO(s->io_base)); | ||
427 | spin_unlock_irqrestore(&s->lock, flags); | ||
428 | if (x == 0xfe) | ||
429 | return 1; | ||
430 | |||
431 | --timeout; | ||
432 | } /* while */ | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * Upload a byte-stream into the SoundScape using DMA channel A. | ||
439 | */ | ||
440 | static int upload_dma_data(struct soundscape *s, | ||
441 | const unsigned char __user *data, | ||
442 | size_t size) | ||
443 | { | ||
444 | unsigned long flags; | ||
445 | struct snd_dma_buffer dma; | ||
446 | int ret; | ||
447 | |||
448 | if (!get_dmabuf(&dma, PAGE_ALIGN(size))) | ||
449 | return -ENOMEM; | ||
450 | |||
451 | spin_lock_irqsave(&s->lock, flags); | ||
452 | |||
453 | /* | ||
454 | * Reset the board ... | ||
455 | */ | ||
456 | sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f); | ||
457 | |||
458 | /* | ||
459 | * Enable the DMA channels and configure them ... | ||
460 | */ | ||
461 | sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50); | ||
462 | sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT); | ||
463 | sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20); | ||
464 | |||
465 | /* | ||
466 | * Take the board out of reset ... | ||
467 | */ | ||
468 | sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80); | ||
469 | |||
470 | /* | ||
471 | * Upload the user's data (firmware?) to the SoundScape | ||
472 | * board through the DMA channel ... | ||
473 | */ | ||
474 | while (size != 0) { | ||
475 | unsigned long len; | ||
476 | |||
477 | /* | ||
478 | * Apparently, copying to/from userspace can sleep. | ||
479 | * We are therefore forbidden from holding any | ||
480 | * spinlocks while we copy ... | ||
481 | */ | ||
482 | spin_unlock_irqrestore(&s->lock, flags); | ||
483 | |||
484 | /* | ||
485 | * Remember that the data that we want to DMA | ||
486 | * comes from USERSPACE. We have already verified | ||
487 | * the userspace pointer ... | ||
488 | */ | ||
489 | len = min(size, dma.bytes); | ||
490 | len -= __copy_from_user(dma.area, data, len); | ||
491 | data += len; | ||
492 | size -= len; | ||
493 | |||
494 | /* | ||
495 | * Grab that spinlock again, now that we've | ||
496 | * finished copying! | ||
497 | */ | ||
498 | spin_lock_irqsave(&s->lock, flags); | ||
499 | |||
500 | snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE); | ||
501 | sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG); | ||
502 | if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) { | ||
503 | /* | ||
504 | * Don't forget to release this spinlock we're holding ... | ||
505 | */ | ||
506 | spin_unlock_irqrestore(&s->lock, flags); | ||
507 | |||
508 | snd_printk(KERN_ERR "sscape: DMA upload has timed out\n"); | ||
509 | ret = -EAGAIN; | ||
510 | goto _release_dma; | ||
511 | } | ||
512 | } /* while */ | ||
513 | |||
514 | set_host_mode_unsafe(s->io_base); | ||
515 | |||
516 | /* | ||
517 | * Boot the board ... (I think) | ||
518 | */ | ||
519 | sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x40); | ||
520 | spin_unlock_irqrestore(&s->lock, flags); | ||
521 | |||
522 | /* | ||
523 | * If all has gone well, then the board should acknowledge | ||
524 | * the new upload and tell us that it has rebooted OK. We | ||
525 | * give it 5 seconds (max) ... | ||
526 | */ | ||
527 | ret = 0; | ||
528 | if (!obp_startup_ack(s, 5)) { | ||
529 | snd_printk(KERN_ERR "sscape: No response from on-board processor after upload\n"); | ||
530 | ret = -EAGAIN; | ||
531 | } else if (!host_startup_ack(s, 5)) { | ||
532 | snd_printk(KERN_ERR "sscape: SoundScape failed to initialise\n"); | ||
533 | ret = -EAGAIN; | ||
534 | } | ||
535 | |||
536 | _release_dma: | ||
537 | /* | ||
538 | * NOTE!!! We are NOT holding any spinlocks at this point !!! | ||
539 | */ | ||
540 | sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_ODIE ? 0x70 : 0x40)); | ||
541 | free_dmabuf(&dma); | ||
542 | |||
543 | return ret; | ||
544 | } | ||
545 | |||
546 | /* | ||
547 | * Upload the bootblock(?) into the SoundScape. The only | ||
548 | * purpose of this block of code seems to be to tell | ||
549 | * us which version of the microcode we should be using. | ||
550 | * | ||
551 | * NOTE: The boot-block data resides in USER-SPACE!!! | ||
552 | * However, we have already verified its memory | ||
553 | * addresses by the time we get here. | ||
554 | */ | ||
555 | static int sscape_upload_bootblock(struct soundscape *sscape, struct sscape_bootblock __user *bb) | ||
556 | { | ||
557 | unsigned long flags; | ||
558 | int data = 0; | ||
559 | int ret; | ||
560 | |||
561 | ret = upload_dma_data(sscape, bb->code, sizeof(bb->code)); | ||
562 | |||
563 | spin_lock_irqsave(&sscape->lock, flags); | ||
564 | if (ret == 0) { | ||
565 | data = host_read_ctrl_unsafe(sscape->io_base, 100); | ||
566 | } | ||
567 | set_midi_mode_unsafe(sscape->io_base); | ||
568 | spin_unlock_irqrestore(&sscape->lock, flags); | ||
569 | |||
570 | if (ret == 0) { | ||
571 | if (data < 0) { | ||
572 | snd_printk(KERN_ERR "sscape: timeout reading firmware version\n"); | ||
573 | ret = -EAGAIN; | ||
574 | } | ||
575 | else if (__copy_to_user(&bb->version, &data, sizeof(bb->version))) { | ||
576 | ret = -EFAULT; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | return ret; | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * Upload the microcode into the SoundScape. The | ||
585 | * microcode is 64K of data, and if we try to copy | ||
586 | * it into a local variable then we will SMASH THE | ||
587 | * KERNEL'S STACK! We therefore leave it in USER | ||
588 | * SPACE, and save ourselves from copying it at all. | ||
589 | */ | ||
590 | static int sscape_upload_microcode(struct soundscape *sscape, | ||
591 | const struct sscape_microcode __user *mc) | ||
592 | { | ||
593 | unsigned long flags; | ||
594 | char __user *code; | ||
595 | int err; | ||
596 | |||
597 | /* | ||
598 | * We are going to have to copy this data into a special | ||
599 | * DMA-able buffer before we can upload it. We shall therefore | ||
600 | * just check that the data pointer is valid for now. | ||
601 | * | ||
602 | * NOTE: This buffer is 64K long! That's WAY too big to | ||
603 | * copy into a stack-temporary anyway. | ||
604 | */ | ||
605 | if ( get_user(code, &mc->code) || | ||
606 | !access_ok(VERIFY_READ, code, SSCAPE_MICROCODE_SIZE) ) | ||
607 | return -EFAULT; | ||
608 | |||
609 | if ((err = upload_dma_data(sscape, code, SSCAPE_MICROCODE_SIZE)) == 0) { | ||
610 | snd_printk(KERN_INFO "sscape: MIDI firmware loaded\n"); | ||
611 | } | ||
612 | |||
613 | spin_lock_irqsave(&sscape->lock, flags); | ||
614 | set_midi_mode_unsafe(sscape->io_base); | ||
615 | spin_unlock_irqrestore(&sscape->lock, flags); | ||
616 | |||
617 | initialise_mpu401(sscape->mpu); | ||
618 | |||
619 | return err; | ||
620 | } | ||
621 | |||
622 | /* | ||
623 | * Hardware-specific device functions, to implement special | ||
624 | * IOCTLs for the SoundScape card. This is how we upload | ||
625 | * the microcode into the card, for example, and so we | ||
626 | * must ensure that no two processes can open this device | ||
627 | * simultaneously, and that we can't open it at all if | ||
628 | * someone is using the MIDI device. | ||
629 | */ | ||
630 | static int sscape_hw_open(snd_hwdep_t * hw, struct file *file) | ||
631 | { | ||
632 | register struct soundscape *sscape = get_hwdep_soundscape(hw); | ||
633 | unsigned long flags; | ||
634 | int err; | ||
635 | |||
636 | spin_lock_irqsave(&sscape->fwlock, flags); | ||
637 | |||
638 | if ((sscape->midi_usage != 0) || sscape->hw_in_use) { | ||
639 | err = -EBUSY; | ||
640 | } else { | ||
641 | sscape->hw_in_use = 1; | ||
642 | err = 0; | ||
643 | } | ||
644 | |||
645 | spin_unlock_irqrestore(&sscape->fwlock, flags); | ||
646 | return err; | ||
647 | } | ||
648 | |||
649 | static int sscape_hw_release(snd_hwdep_t * hw, struct file *file) | ||
650 | { | ||
651 | register struct soundscape *sscape = get_hwdep_soundscape(hw); | ||
652 | unsigned long flags; | ||
653 | |||
654 | spin_lock_irqsave(&sscape->fwlock, flags); | ||
655 | sscape->hw_in_use = 0; | ||
656 | spin_unlock_irqrestore(&sscape->fwlock, flags); | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static int sscape_hw_ioctl(snd_hwdep_t * hw, struct file *file, | ||
661 | unsigned int cmd, unsigned long arg) | ||
662 | { | ||
663 | struct soundscape *sscape = get_hwdep_soundscape(hw); | ||
664 | int err = -EBUSY; | ||
665 | |||
666 | switch (cmd) { | ||
667 | case SND_SSCAPE_LOAD_BOOTB: | ||
668 | { | ||
669 | register struct sscape_bootblock __user *bb = (struct sscape_bootblock __user *) arg; | ||
670 | |||
671 | /* | ||
672 | * We are going to have to copy this data into a special | ||
673 | * DMA-able buffer before we can upload it. We shall therefore | ||
674 | * just check that the data pointer is valid for now ... | ||
675 | */ | ||
676 | if ( !access_ok(VERIFY_READ, bb->code, sizeof(bb->code)) ) | ||
677 | return -EFAULT; | ||
678 | |||
679 | /* | ||
680 | * Now check that we can write the firmware version number too... | ||
681 | */ | ||
682 | if ( !access_ok(VERIFY_WRITE, &bb->version, sizeof(bb->version)) ) | ||
683 | return -EFAULT; | ||
684 | |||
685 | err = sscape_upload_bootblock(sscape, bb); | ||
686 | } | ||
687 | break; | ||
688 | |||
689 | case SND_SSCAPE_LOAD_MCODE: | ||
690 | { | ||
691 | register const struct sscape_microcode __user *mc = (const struct sscape_microcode __user *) arg; | ||
692 | |||
693 | err = sscape_upload_microcode(sscape, mc); | ||
694 | } | ||
695 | break; | ||
696 | |||
697 | default: | ||
698 | err = -EINVAL; | ||
699 | break; | ||
700 | } /* switch */ | ||
701 | |||
702 | return err; | ||
703 | } | ||
704 | |||
705 | |||
706 | /* | ||
707 | * Mixer control for the SoundScape's MIDI device. | ||
708 | */ | ||
709 | static int sscape_midi_info(snd_kcontrol_t * ctl, | ||
710 | snd_ctl_elem_info_t * uinfo) | ||
711 | { | ||
712 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
713 | uinfo->count = 1; | ||
714 | uinfo->value.integer.min = 0; | ||
715 | uinfo->value.integer.max = 127; | ||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | static int sscape_midi_get(snd_kcontrol_t * kctl, | ||
720 | snd_ctl_elem_value_t * uctl) | ||
721 | { | ||
722 | cs4231_t *chip = snd_kcontrol_chip(kctl); | ||
723 | snd_card_t *card = chip->card; | ||
724 | register struct soundscape *s = get_card_soundscape(card); | ||
725 | unsigned long flags; | ||
726 | |||
727 | spin_lock_irqsave(&s->lock, flags); | ||
728 | set_host_mode_unsafe(s->io_base); | ||
729 | |||
730 | if (host_write_ctrl_unsafe(s->io_base, CMD_GET_MIDI_VOL, 100)) { | ||
731 | uctl->value.integer.value[0] = host_read_ctrl_unsafe(s->io_base, 100); | ||
732 | } | ||
733 | |||
734 | set_midi_mode_unsafe(s->io_base); | ||
735 | spin_unlock_irqrestore(&s->lock, flags); | ||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | static int sscape_midi_put(snd_kcontrol_t * kctl, | ||
740 | snd_ctl_elem_value_t * uctl) | ||
741 | { | ||
742 | cs4231_t *chip = snd_kcontrol_chip(kctl); | ||
743 | snd_card_t *card = chip->card; | ||
744 | register struct soundscape *s = get_card_soundscape(card); | ||
745 | unsigned long flags; | ||
746 | int change; | ||
747 | |||
748 | spin_lock_irqsave(&s->lock, flags); | ||
749 | |||
750 | /* | ||
751 | * We need to put the board into HOST mode before we | ||
752 | * can send any volume-changing HOST commands ... | ||
753 | */ | ||
754 | set_host_mode_unsafe(s->io_base); | ||
755 | |||
756 | /* | ||
757 | * To successfully change the MIDI volume setting, you seem to | ||
758 | * have to write a volume command, write the new volume value, | ||
759 | * and then perform another volume-related command. Perhaps the | ||
760 | * first command is an "open" and the second command is a "close"? | ||
761 | */ | ||
762 | if (s->midi_vol == ((unsigned char) uctl->value.integer. value[0] & 127)) { | ||
763 | change = 0; | ||
764 | goto __skip_change; | ||
765 | } | ||
766 | change = (host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100) | ||
767 | && host_write_ctrl_unsafe(s->io_base, ((unsigned char) uctl->value.integer. value[0]) & 127, 100) | ||
768 | && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100)); | ||
769 | __skip_change: | ||
770 | |||
771 | /* | ||
772 | * Take the board out of HOST mode and back into MIDI mode ... | ||
773 | */ | ||
774 | set_midi_mode_unsafe(s->io_base); | ||
775 | |||
776 | spin_unlock_irqrestore(&s->lock, flags); | ||
777 | return change; | ||
778 | } | ||
779 | |||
780 | static snd_kcontrol_new_t midi_mixer_ctl = { | ||
781 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
782 | .name = "MIDI", | ||
783 | .info = sscape_midi_info, | ||
784 | .get = sscape_midi_get, | ||
785 | .put = sscape_midi_put | ||
786 | }; | ||
787 | |||
788 | /* | ||
789 | * The SoundScape can use two IRQs from a possible set of four. | ||
790 | * These IRQs are encoded as bit patterns so that they can be | ||
791 | * written to the control registers. | ||
792 | */ | ||
793 | static unsigned __devinit get_irq_config(int irq) | ||
794 | { | ||
795 | static const int valid_irq[] = { 9, 5, 7, 10 }; | ||
796 | unsigned cfg; | ||
797 | |||
798 | for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg) { | ||
799 | if (irq == valid_irq[cfg]) | ||
800 | return cfg; | ||
801 | } /* for */ | ||
802 | |||
803 | return INVALID_IRQ; | ||
804 | } | ||
805 | |||
806 | |||
807 | /* | ||
808 | * Perform certain arcane port-checks to see whether there | ||
809 | * is a SoundScape board lurking behind the given ports. | ||
810 | */ | ||
811 | static int __devinit detect_sscape(struct soundscape *s) | ||
812 | { | ||
813 | unsigned long flags; | ||
814 | unsigned d; | ||
815 | int retval = 0; | ||
816 | |||
817 | spin_lock_irqsave(&s->lock, flags); | ||
818 | |||
819 | /* | ||
820 | * The following code is lifted from the original OSS driver, | ||
821 | * and as I don't have a datasheet I cannot really comment | ||
822 | * on what it is doing... | ||
823 | */ | ||
824 | if ((inb(HOST_CTRL_IO(s->io_base)) & 0x78) != 0) | ||
825 | goto _done; | ||
826 | |||
827 | d = inb(ODIE_ADDR_IO(s->io_base)) & 0xf0; | ||
828 | if ((d & 0x80) != 0) | ||
829 | goto _done; | ||
830 | |||
831 | if (d == 0) { | ||
832 | s->codec_type = 1; | ||
833 | s->ic_type = IC_ODIE; | ||
834 | } else if ((d & 0x60) != 0) { | ||
835 | s->codec_type = 2; | ||
836 | s->ic_type = IC_OPUS; | ||
837 | } else | ||
838 | goto _done; | ||
839 | |||
840 | outb(0xfa, ODIE_ADDR_IO(s->io_base)); | ||
841 | if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0a) | ||
842 | goto _done; | ||
843 | |||
844 | outb(0xfe, ODIE_ADDR_IO(s->io_base)); | ||
845 | if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0e) | ||
846 | goto _done; | ||
847 | if ((inb(ODIE_DATA_IO(s->io_base)) & 0x9f) != 0x0e) | ||
848 | goto _done; | ||
849 | |||
850 | /* | ||
851 | * SoundScape successfully detected! | ||
852 | */ | ||
853 | retval = 1; | ||
854 | |||
855 | _done: | ||
856 | spin_unlock_irqrestore(&s->lock, flags); | ||
857 | return retval; | ||
858 | } | ||
859 | |||
860 | /* | ||
861 | * ALSA callback function, called when attempting to open the MIDI device. | ||
862 | * Check that the MIDI firmware has been loaded, because we don't want | ||
863 | * to crash the machine. Also check that someone isn't using the hardware | ||
864 | * IOCTL device. | ||
865 | */ | ||
866 | static int mpu401_open(mpu401_t * mpu) | ||
867 | { | ||
868 | int err; | ||
869 | |||
870 | if (!verify_mpu401(mpu)) { | ||
871 | snd_printk(KERN_ERR "sscape: MIDI disabled, please load firmware\n"); | ||
872 | err = -ENODEV; | ||
873 | } else { | ||
874 | register struct soundscape *sscape = get_mpu401_soundscape(mpu); | ||
875 | unsigned long flags; | ||
876 | |||
877 | spin_lock_irqsave(&sscape->fwlock, flags); | ||
878 | |||
879 | if (sscape->hw_in_use || (sscape->midi_usage == ULONG_MAX)) { | ||
880 | err = -EBUSY; | ||
881 | } else { | ||
882 | ++(sscape->midi_usage); | ||
883 | err = 0; | ||
884 | } | ||
885 | |||
886 | spin_unlock_irqrestore(&sscape->fwlock, flags); | ||
887 | } | ||
888 | |||
889 | return err; | ||
890 | } | ||
891 | |||
892 | static void mpu401_close(mpu401_t * mpu) | ||
893 | { | ||
894 | register struct soundscape *sscape = get_mpu401_soundscape(mpu); | ||
895 | unsigned long flags; | ||
896 | |||
897 | spin_lock_irqsave(&sscape->fwlock, flags); | ||
898 | --(sscape->midi_usage); | ||
899 | spin_unlock_irqrestore(&sscape->fwlock, flags); | ||
900 | } | ||
901 | |||
902 | /* | ||
903 | * Initialse an MPU-401 subdevice for MIDI support on the SoundScape. | ||
904 | */ | ||
905 | static int __devinit create_mpu401(snd_card_t * card, int devnum, unsigned long port, int irq) | ||
906 | { | ||
907 | struct soundscape *sscape = get_card_soundscape(card); | ||
908 | snd_rawmidi_t *rawmidi; | ||
909 | int err; | ||
910 | |||
911 | #define MPU401_SHARE_HARDWARE 1 | ||
912 | if ((err = snd_mpu401_uart_new(card, devnum, | ||
913 | MPU401_HW_MPU401, | ||
914 | port, MPU401_SHARE_HARDWARE, | ||
915 | irq, SA_INTERRUPT, | ||
916 | &rawmidi)) == 0) { | ||
917 | mpu401_t *mpu = (mpu401_t *) rawmidi->private_data; | ||
918 | mpu->open_input = mpu401_open; | ||
919 | mpu->open_output = mpu401_open; | ||
920 | mpu->close_input = mpu401_close; | ||
921 | mpu->close_output = mpu401_close; | ||
922 | mpu->private_data = sscape; | ||
923 | sscape->mpu = mpu; | ||
924 | |||
925 | initialise_mpu401(mpu); | ||
926 | } | ||
927 | |||
928 | return err; | ||
929 | } | ||
930 | |||
931 | |||
932 | /* | ||
933 | * Override for the CS4231 playback format function. | ||
934 | * The AD1845 has much simpler format and rate selection. | ||
935 | */ | ||
936 | static void ad1845_playback_format(cs4231_t * chip, snd_pcm_hw_params_t * params, unsigned char format) | ||
937 | { | ||
938 | unsigned long flags; | ||
939 | unsigned rate = params_rate(params); | ||
940 | |||
941 | /* | ||
942 | * The AD1845 can't handle sample frequencies | ||
943 | * outside of 4 kHZ to 50 kHZ | ||
944 | */ | ||
945 | if (rate > 50000) | ||
946 | rate = 50000; | ||
947 | else if (rate < 4000) | ||
948 | rate = 4000; | ||
949 | |||
950 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
951 | |||
952 | /* | ||
953 | * Program the AD1845 correctly for the playback stream. | ||
954 | * Note that we do NOT need to toggle the MCE bit because | ||
955 | * the PLAYBACK_ENABLE bit of the Interface Configuration | ||
956 | * register is set. | ||
957 | * | ||
958 | * NOTE: We seem to need to write to the MSB before the LSB | ||
959 | * to get the correct sample frequency. | ||
960 | */ | ||
961 | snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0)); | ||
962 | snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8)); | ||
963 | snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate); | ||
964 | |||
965 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
966 | } | ||
967 | |||
968 | /* | ||
969 | * Override for the CS4231 capture format function. | ||
970 | * The AD1845 has much simpler format and rate selection. | ||
971 | */ | ||
972 | static void ad1845_capture_format(cs4231_t * chip, snd_pcm_hw_params_t * params, unsigned char format) | ||
973 | { | ||
974 | unsigned long flags; | ||
975 | unsigned rate = params_rate(params); | ||
976 | |||
977 | /* | ||
978 | * The AD1845 can't handle sample frequencies | ||
979 | * outside of 4 kHZ to 50 kHZ | ||
980 | */ | ||
981 | if (rate > 50000) | ||
982 | rate = 50000; | ||
983 | else if (rate < 4000) | ||
984 | rate = 4000; | ||
985 | |||
986 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
987 | |||
988 | /* | ||
989 | * Program the AD1845 correctly for the playback stream. | ||
990 | * Note that we do NOT need to toggle the MCE bit because | ||
991 | * the CAPTURE_ENABLE bit of the Interface Configuration | ||
992 | * register is set. | ||
993 | * | ||
994 | * NOTE: We seem to need to write to the MSB before the LSB | ||
995 | * to get the correct sample frequency. | ||
996 | */ | ||
997 | snd_cs4231_out(chip, CS4231_REC_FORMAT, (format & 0xf0)); | ||
998 | snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8)); | ||
999 | snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate); | ||
1000 | |||
1001 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1002 | } | ||
1003 | |||
1004 | /* | ||
1005 | * Create an AD1845 PCM subdevice on the SoundScape. The AD1845 | ||
1006 | * is very much like a CS4231, with a few extra bits. We will | ||
1007 | * try to support at least some of the extra bits by overriding | ||
1008 | * some of the CS4231 callback. | ||
1009 | */ | ||
1010 | static int __devinit create_ad1845(snd_card_t * card, unsigned port, int irq, int dma1) | ||
1011 | { | ||
1012 | register struct soundscape *sscape = get_card_soundscape(card); | ||
1013 | cs4231_t *chip; | ||
1014 | int err; | ||
1015 | |||
1016 | #define CS4231_SHARE_HARDWARE (CS4231_HWSHARE_DMA1 | CS4231_HWSHARE_DMA2) | ||
1017 | /* | ||
1018 | * The AD1845 PCM device is only half-duplex, and so | ||
1019 | * we only give it one DMA channel ... | ||
1020 | */ | ||
1021 | if ((err = snd_cs4231_create(card, | ||
1022 | port, -1, irq, dma1, dma1, | ||
1023 | CS4231_HW_DETECT, | ||
1024 | CS4231_HWSHARE_DMA1, &chip)) == 0) { | ||
1025 | unsigned long flags; | ||
1026 | snd_pcm_t *pcm; | ||
1027 | |||
1028 | #define AD1845_FREQ_SEL_ENABLE 0x08 | ||
1029 | |||
1030 | #define AD1845_PWR_DOWN_CTRL 0x1b | ||
1031 | #define AD1845_CRYS_CLOCK_SEL 0x1d | ||
1032 | |||
1033 | /* | ||
1034 | * It turns out that the PLAYBACK_ENABLE bit is set | ||
1035 | * by the lowlevel driver ... | ||
1036 | * | ||
1037 | #define AD1845_IFACE_CONFIG \ | ||
1038 | (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE) | ||
1039 | snd_cs4231_mce_up(chip); | ||
1040 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1041 | snd_cs4231_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG); | ||
1042 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1043 | snd_cs4231_mce_down(chip); | ||
1044 | */ | ||
1045 | |||
1046 | /* | ||
1047 | * The input clock frequency on the SoundScape must | ||
1048 | * be 14.31818 MHz, because we must set this register | ||
1049 | * to get the playback to sound correct ... | ||
1050 | */ | ||
1051 | snd_cs4231_mce_up(chip); | ||
1052 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1053 | snd_cs4231_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20); | ||
1054 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1055 | snd_cs4231_mce_down(chip); | ||
1056 | |||
1057 | /* | ||
1058 | * More custom configuration: | ||
1059 | * a) select "mode 2", and provide a current drive of 8 mA | ||
1060 | * b) enable frequency selection (for capture/playback) | ||
1061 | */ | ||
1062 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1063 | snd_cs4231_out(chip, CS4231_MISC_INFO, (CS4231_MODE2 | 0x10)); | ||
1064 | snd_cs4231_out(chip, AD1845_PWR_DOWN_CTRL, snd_cs4231_in(chip, AD1845_PWR_DOWN_CTRL) | AD1845_FREQ_SEL_ENABLE); | ||
1065 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1066 | |||
1067 | if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { | ||
1068 | snd_printk(KERN_ERR "sscape: No PCM device for AD1845 chip\n"); | ||
1069 | goto _error; | ||
1070 | } | ||
1071 | |||
1072 | if ((err = snd_cs4231_mixer(chip)) < 0) { | ||
1073 | snd_printk(KERN_ERR "sscape: No mixer device for AD1845 chip\n"); | ||
1074 | goto _error; | ||
1075 | } | ||
1076 | |||
1077 | if ((err = snd_ctl_add(card, snd_ctl_new1(&midi_mixer_ctl, chip))) < 0) { | ||
1078 | snd_printk(KERN_ERR "sscape: Could not create MIDI mixer control\n"); | ||
1079 | goto _error; | ||
1080 | } | ||
1081 | |||
1082 | strcpy(card->driver, "SoundScape"); | ||
1083 | strcpy(card->shortname, pcm->name); | ||
1084 | snprintf(card->longname, sizeof(card->longname), | ||
1085 | "%s at 0x%lx, IRQ %d, DMA %d\n", | ||
1086 | pcm->name, chip->port, chip->irq, chip->dma1); | ||
1087 | chip->set_playback_format = ad1845_playback_format; | ||
1088 | chip->set_capture_format = ad1845_capture_format; | ||
1089 | sscape->chip = chip; | ||
1090 | } | ||
1091 | |||
1092 | _error: | ||
1093 | return err; | ||
1094 | } | ||
1095 | |||
1096 | |||
1097 | struct params | ||
1098 | { | ||
1099 | int index; | ||
1100 | const char *id; | ||
1101 | unsigned port; | ||
1102 | int irq; | ||
1103 | int mpu_irq; | ||
1104 | int dma1; | ||
1105 | }; | ||
1106 | |||
1107 | |||
1108 | static inline struct params* | ||
1109 | init_params(struct params *params, | ||
1110 | int index, | ||
1111 | const char *id, | ||
1112 | unsigned port, | ||
1113 | int irq, | ||
1114 | int mpu_irq, | ||
1115 | int dma1) | ||
1116 | { | ||
1117 | params->index = index; | ||
1118 | params->id = id; | ||
1119 | params->port = port; | ||
1120 | params->irq = irq; | ||
1121 | params->mpu_irq = mpu_irq; | ||
1122 | params->dma1 = (dma1 & 0x03); | ||
1123 | |||
1124 | return params; | ||
1125 | } | ||
1126 | |||
1127 | |||
1128 | /* | ||
1129 | * Create an ALSA soundcard entry for the SoundScape, using | ||
1130 | * the given list of port, IRQ and DMA resources. | ||
1131 | */ | ||
1132 | static int __devinit create_sscape(const struct params *params, snd_card_t **rcardp) | ||
1133 | { | ||
1134 | snd_card_t *card; | ||
1135 | register struct soundscape *sscape; | ||
1136 | register unsigned dma_cfg; | ||
1137 | unsigned irq_cfg; | ||
1138 | unsigned mpu_irq_cfg; | ||
1139 | struct resource *io_res; | ||
1140 | unsigned long flags; | ||
1141 | int err; | ||
1142 | |||
1143 | /* | ||
1144 | * Check that the user didn't pass us garbage data ... | ||
1145 | */ | ||
1146 | irq_cfg = get_irq_config(params->irq); | ||
1147 | if (irq_cfg == INVALID_IRQ) { | ||
1148 | snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", params->irq); | ||
1149 | return -ENXIO; | ||
1150 | } | ||
1151 | |||
1152 | mpu_irq_cfg = get_irq_config(params->mpu_irq); | ||
1153 | if (mpu_irq_cfg == INVALID_IRQ) { | ||
1154 | printk(KERN_ERR "sscape: Invalid IRQ %d\n", params->mpu_irq); | ||
1155 | return -ENXIO; | ||
1156 | } | ||
1157 | |||
1158 | /* | ||
1159 | * Grab IO ports that we will need to probe so that we | ||
1160 | * can detect and control this hardware ... | ||
1161 | */ | ||
1162 | if ((io_res = request_region(params->port, 8, "SoundScape")) == NULL) { | ||
1163 | snd_printk(KERN_ERR "sscape: can't grab port 0x%x\n", params->port); | ||
1164 | return -EBUSY; | ||
1165 | } | ||
1166 | |||
1167 | /* | ||
1168 | * Grab both DMA channels (OK, only one for now) ... | ||
1169 | */ | ||
1170 | if ((err = request_dma(params->dma1, "SoundScape")) < 0) { | ||
1171 | snd_printk(KERN_ERR "sscape: can't grab DMA %d\n", params->dma1); | ||
1172 | goto _release_region; | ||
1173 | } | ||
1174 | |||
1175 | /* | ||
1176 | * Create a new ALSA sound card entry, in anticipation | ||
1177 | * of detecting our hardware ... | ||
1178 | */ | ||
1179 | if ((card = snd_card_new(params->index, params->id, THIS_MODULE, sizeof(struct soundscape))) == NULL) { | ||
1180 | err = -ENOMEM; | ||
1181 | goto _release_dma; | ||
1182 | } | ||
1183 | |||
1184 | sscape = get_card_soundscape(card); | ||
1185 | spin_lock_init(&sscape->lock); | ||
1186 | spin_lock_init(&sscape->fwlock); | ||
1187 | sscape->io_res = io_res; | ||
1188 | sscape->io_base = params->port; | ||
1189 | |||
1190 | if (!detect_sscape(sscape)) { | ||
1191 | printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base); | ||
1192 | err = -ENODEV; | ||
1193 | goto _release_card; | ||
1194 | } | ||
1195 | |||
1196 | printk(KERN_INFO "sscape: hardware detected at 0x%x, using IRQ %d, DMA %d\n", | ||
1197 | sscape->io_base, params->irq, params->dma1); | ||
1198 | |||
1199 | /* | ||
1200 | * Now create the hardware-specific device so that we can | ||
1201 | * load the microcode into the on-board processor. | ||
1202 | * We cannot use the MPU-401 MIDI system until this firmware | ||
1203 | * has been loaded into the card. | ||
1204 | */ | ||
1205 | if ((err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw))) < 0) { | ||
1206 | printk(KERN_ERR "sscape: Failed to create firmware device\n"); | ||
1207 | goto _release_card; | ||
1208 | } | ||
1209 | strlcpy(sscape->hw->name, "SoundScape M68K", sizeof(sscape->hw->name)); | ||
1210 | sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0'; | ||
1211 | sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE; | ||
1212 | sscape->hw->ops.open = sscape_hw_open; | ||
1213 | sscape->hw->ops.release = sscape_hw_release; | ||
1214 | sscape->hw->ops.ioctl = sscape_hw_ioctl; | ||
1215 | sscape->hw->private_data = sscape; | ||
1216 | |||
1217 | /* | ||
1218 | * Tell the on-board devices where their resources are (I think - | ||
1219 | * I can't be sure without a datasheet ... So many magic values!) | ||
1220 | */ | ||
1221 | spin_lock_irqsave(&sscape->lock, flags); | ||
1222 | |||
1223 | activate_ad1845_unsafe(sscape->io_base); | ||
1224 | |||
1225 | sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x00); /* disable */ | ||
1226 | sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); | ||
1227 | sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); | ||
1228 | |||
1229 | /* | ||
1230 | * Enable and configure the DMA channels ... | ||
1231 | */ | ||
1232 | sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); | ||
1233 | dma_cfg = (sscape->ic_type == IC_ODIE ? 0x70 : 0x40); | ||
1234 | sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); | ||
1235 | sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); | ||
1236 | |||
1237 | sscape_write_unsafe(sscape->io_base, | ||
1238 | GA_INTCFG_REG, 0xf0 | (mpu_irq_cfg << 2) | mpu_irq_cfg); | ||
1239 | sscape_write_unsafe(sscape->io_base, | ||
1240 | GA_CDCFG_REG, 0x09 | DMA_8BIT | (params->dma1 << 4) | (irq_cfg << 1)); | ||
1241 | |||
1242 | spin_unlock_irqrestore(&sscape->lock, flags); | ||
1243 | |||
1244 | /* | ||
1245 | * We have now enabled the codec chip, and so we should | ||
1246 | * detect the AD1845 device ... | ||
1247 | */ | ||
1248 | if ((err = create_ad1845(card, CODEC_IO(params->port), params->irq, params->dma1)) < 0) { | ||
1249 | printk(KERN_ERR "sscape: No AD1845 device at 0x%x, IRQ %d\n", | ||
1250 | CODEC_IO(params->port), params->irq); | ||
1251 | goto _release_card; | ||
1252 | } | ||
1253 | #define MIDI_DEVNUM 0 | ||
1254 | if ((err = create_mpu401(card, MIDI_DEVNUM, MPU401_IO(params->port), params->mpu_irq)) < 0) { | ||
1255 | printk(KERN_ERR "sscape: Failed to create MPU-401 device at 0x%x\n", | ||
1256 | MPU401_IO(params->port)); | ||
1257 | goto _release_card; | ||
1258 | } | ||
1259 | |||
1260 | /* | ||
1261 | * Enable the master IRQ ... | ||
1262 | */ | ||
1263 | sscape_write(sscape, GA_INTENA_REG, 0x80); | ||
1264 | |||
1265 | if ((err = snd_card_register(card)) < 0) { | ||
1266 | printk(KERN_ERR "sscape: Failed to register sound card\n"); | ||
1267 | goto _release_card; | ||
1268 | } | ||
1269 | |||
1270 | /* | ||
1271 | * Initialize mixer | ||
1272 | */ | ||
1273 | sscape->midi_vol = 0; | ||
1274 | host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100); | ||
1275 | host_write_ctrl_unsafe(sscape->io_base, 0, 100); | ||
1276 | host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100); | ||
1277 | |||
1278 | /* | ||
1279 | * Now that we have successfully created this sound card, | ||
1280 | * it is safe to store the pointer. | ||
1281 | * NOTE: we only register the sound card's "destructor" | ||
1282 | * function now that our "constructor" has completed. | ||
1283 | */ | ||
1284 | card->private_free = soundscape_free; | ||
1285 | *rcardp = card; | ||
1286 | |||
1287 | return 0; | ||
1288 | |||
1289 | _release_card: | ||
1290 | snd_card_free(card); | ||
1291 | |||
1292 | _release_dma: | ||
1293 | free_dma(params->dma1); | ||
1294 | |||
1295 | _release_region: | ||
1296 | release_resource(io_res); | ||
1297 | kfree_nocheck(io_res); | ||
1298 | |||
1299 | return err; | ||
1300 | } | ||
1301 | |||
1302 | |||
1303 | static int sscape_cards __devinitdata; | ||
1304 | static struct params sscape_params[SNDRV_CARDS] __devinitdata; | ||
1305 | |||
1306 | #ifdef CONFIG_PNP | ||
1307 | static inline int __devinit get_next_autoindex(int i) | ||
1308 | { | ||
1309 | while ((i < SNDRV_CARDS) && (port[i] != SNDRV_AUTO_PORT)) { | ||
1310 | ++i; | ||
1311 | } /* while */ | ||
1312 | |||
1313 | return i; | ||
1314 | } | ||
1315 | |||
1316 | |||
1317 | static inline int __devinit is_port_known(unsigned io, struct params *params, int cards) | ||
1318 | { | ||
1319 | while (--cards >= 0) { | ||
1320 | if (params[cards].port == io) | ||
1321 | return 1; | ||
1322 | } /* while */ | ||
1323 | |||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard, | ||
1328 | const struct pnp_card_device_id *pid) | ||
1329 | { | ||
1330 | struct pnp_dev *dev; | ||
1331 | static int idx = 0; | ||
1332 | int ret; | ||
1333 | |||
1334 | /* | ||
1335 | * Allow this function to fail *quietly* if all the ISA PnP | ||
1336 | * devices were configured using module parameters instead. | ||
1337 | */ | ||
1338 | if ((idx = get_next_autoindex(idx)) >= SNDRV_CARDS) { | ||
1339 | return -ENOSPC; | ||
1340 | } | ||
1341 | |||
1342 | /* | ||
1343 | * We have found a candidate ISA PnP card. Now we | ||
1344 | * have to check that it has the devices that we | ||
1345 | * expect it to have. | ||
1346 | * | ||
1347 | * We will NOT try and autoconfigure all of the resources | ||
1348 | * needed and then activate the card as we are assuming that | ||
1349 | * has already been done at boot-time using /proc/isapnp. | ||
1350 | * We shall simply try to give each active card the resources | ||
1351 | * that it wants. This is a sensible strategy for a modular | ||
1352 | * system where unused modules are unloaded regularly. | ||
1353 | * | ||
1354 | * This strategy is utterly useless if we compile the driver | ||
1355 | * into the kernel, of course. | ||
1356 | */ | ||
1357 | // printk(KERN_INFO "sscape: %s\n", card->name); | ||
1358 | |||
1359 | /* | ||
1360 | * Check that we still have room for another sound card ... | ||
1361 | */ | ||
1362 | if (sscape_cards >= SNDRV_CARDS) { | ||
1363 | printk(KERN_ERR "sscape: No room for another ALSA device\n"); | ||
1364 | return -ENOSPC; | ||
1365 | } | ||
1366 | |||
1367 | ret = -ENODEV; | ||
1368 | |||
1369 | dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL); | ||
1370 | if (dev) { | ||
1371 | struct params *this; | ||
1372 | if (!pnp_is_active(dev)) { | ||
1373 | if (pnp_activate_dev(dev) < 0) { | ||
1374 | printk(KERN_INFO "sscape: device is inactive\n"); | ||
1375 | return -EBUSY; | ||
1376 | } | ||
1377 | } | ||
1378 | /* | ||
1379 | * Read the correct parameters off the ISA PnP bus ... | ||
1380 | */ | ||
1381 | this = init_params(&sscape_params[sscape_cards], | ||
1382 | index[idx], | ||
1383 | id[idx], | ||
1384 | pnp_port_start(dev, 0), | ||
1385 | pnp_irq(dev, 0), | ||
1386 | pnp_irq(dev, 1), | ||
1387 | pnp_dma(dev, 0)); | ||
1388 | |||
1389 | /* | ||
1390 | * Do we know about this sound card already? | ||
1391 | */ | ||
1392 | if ( !is_port_known(this->port, sscape_params, sscape_cards) ) { | ||
1393 | snd_card_t *card; | ||
1394 | |||
1395 | ret = create_sscape(this, &card); | ||
1396 | if (ret < 0) | ||
1397 | return ret; | ||
1398 | snd_card_set_dev(card, &pcard->card->dev); | ||
1399 | pnp_set_card_drvdata(pcard, card); | ||
1400 | ++sscape_cards; | ||
1401 | ++idx; | ||
1402 | } | ||
1403 | } | ||
1404 | |||
1405 | return ret; | ||
1406 | } | ||
1407 | |||
1408 | static void __devexit sscape_pnp_remove(struct pnp_card_link * pcard) | ||
1409 | { | ||
1410 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
1411 | |||
1412 | pnp_set_card_drvdata(pcard, NULL); | ||
1413 | snd_card_disconnect(card); | ||
1414 | snd_card_free_in_thread(card); | ||
1415 | } | ||
1416 | |||
1417 | static struct pnp_card_driver sscape_pnpc_driver = { | ||
1418 | .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, | ||
1419 | .name = "sscape", | ||
1420 | .id_table = sscape_pnpids, | ||
1421 | .probe = sscape_pnp_detect, | ||
1422 | .remove = __devexit_p(sscape_pnp_remove), | ||
1423 | }; | ||
1424 | |||
1425 | #endif /* CONFIG_PNP */ | ||
1426 | |||
1427 | static int __init sscape_manual_probe(struct params *params) | ||
1428 | { | ||
1429 | int ret; | ||
1430 | unsigned i; | ||
1431 | snd_card_t *card; | ||
1432 | |||
1433 | for (i = 0; i < SNDRV_CARDS; ++i) { | ||
1434 | /* | ||
1435 | * We do NOT probe for ports. | ||
1436 | * If we're not given a port number for this | ||
1437 | * card then we completely ignore this line | ||
1438 | * of parameters. | ||
1439 | */ | ||
1440 | if (port[i] == SNDRV_AUTO_PORT) | ||
1441 | continue; | ||
1442 | |||
1443 | /* | ||
1444 | * Make sure we were given ALL of the other parameters. | ||
1445 | */ | ||
1446 | if ( (irq[i] == SNDRV_AUTO_IRQ) || | ||
1447 | (mpu_irq[i] == SNDRV_AUTO_IRQ) || | ||
1448 | (dma[i] == SNDRV_AUTO_DMA) ) { | ||
1449 | printk(KERN_INFO | ||
1450 | "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n"); | ||
1451 | return -ENXIO; | ||
1452 | } | ||
1453 | |||
1454 | /* | ||
1455 | * This cards looks OK ... | ||
1456 | */ | ||
1457 | init_params(params, index[i], id[i], port[i], irq[i], mpu_irq[i], dma[i]); | ||
1458 | |||
1459 | ret = create_sscape(params, &card); | ||
1460 | if (ret < 0) | ||
1461 | return ret; | ||
1462 | |||
1463 | sscape_card[sscape_cards] = card; | ||
1464 | params++; | ||
1465 | sscape_cards++; | ||
1466 | } /* for */ | ||
1467 | |||
1468 | return 0; | ||
1469 | } | ||
1470 | |||
1471 | |||
1472 | static void sscape_exit(void) | ||
1473 | { | ||
1474 | unsigned i; | ||
1475 | |||
1476 | #ifdef CONFIG_PNP | ||
1477 | pnp_unregister_card_driver(&sscape_pnpc_driver); | ||
1478 | #endif | ||
1479 | for (i = 0; i < ARRAY_SIZE(sscape_card); ++i) { | ||
1480 | snd_card_free(sscape_card[i]); | ||
1481 | } /* for */ | ||
1482 | } | ||
1483 | |||
1484 | |||
1485 | static int __init sscape_init(void) | ||
1486 | { | ||
1487 | int ret; | ||
1488 | |||
1489 | /* | ||
1490 | * First check whether we were passed any parameters. | ||
1491 | * These MUST take precedence over ANY automatic way | ||
1492 | * of allocating cards, because the operator is | ||
1493 | * S-P-E-L-L-I-N-G it out for us... | ||
1494 | */ | ||
1495 | ret = sscape_manual_probe(sscape_params); | ||
1496 | if (ret < 0) { | ||
1497 | int i; | ||
1498 | for (i = 0; i < sscape_cards; ++i) | ||
1499 | snd_card_free(sscape_card[i]); | ||
1500 | return ret; | ||
1501 | } | ||
1502 | |||
1503 | #ifdef CONFIG_PNP | ||
1504 | if (sscape_cards < SNDRV_CARDS) { | ||
1505 | ret = pnp_register_card_driver(&sscape_pnpc_driver); | ||
1506 | if (ret < 0) { | ||
1507 | sscape_exit(); | ||
1508 | return ret; | ||
1509 | } | ||
1510 | } | ||
1511 | #endif | ||
1512 | |||
1513 | return 0; | ||
1514 | } | ||
1515 | |||
1516 | module_init(sscape_init); | ||
1517 | module_exit(sscape_exit); | ||
diff --git a/sound/isa/wavefront/Makefile b/sound/isa/wavefront/Makefile new file mode 100644 index 000000000000..b4cb28422db0 --- /dev/null +++ b/sound/isa/wavefront/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o | ||
7 | |||
8 | # Toplevel Module Dependency | ||
9 | obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o | ||
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c new file mode 100644 index 000000000000..79b022070ba3 --- /dev/null +++ b/sound/isa/wavefront/wavefront.c | |||
@@ -0,0 +1,716 @@ | |||
1 | /* | ||
2 | * ALSA card-level driver for Turtle Beach Wavefront cards | ||
3 | * (Maui,Tropez,Tropez+) | ||
4 | * | ||
5 | * Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/pnp.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/initval.h> | ||
30 | #include <sound/opl3.h> | ||
31 | #include <sound/snd_wavefront.h> | ||
32 | |||
33 | MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>"); | ||
34 | MODULE_DESCRIPTION("Turtle Beach Wavefront"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | MODULE_SUPPORTED_DEVICE("{{Turtle Beach,Maui/Tropez/Tropez+}}"); | ||
37 | |||
38 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
39 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
40 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
41 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
42 | static long cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
43 | static int cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ | ||
44 | static long cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
45 | static int cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ | ||
46 | static long ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
47 | static int ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */ | ||
48 | static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ | ||
49 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
50 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
51 | static int use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; | ||
52 | |||
53 | module_param_array(index, int, NULL, 0444); | ||
54 | MODULE_PARM_DESC(index, "Index value for WaveFront soundcard."); | ||
55 | module_param_array(id, charp, NULL, 0444); | ||
56 | MODULE_PARM_DESC(id, "ID string for WaveFront soundcard."); | ||
57 | module_param_array(enable, bool, NULL, 0444); | ||
58 | MODULE_PARM_DESC(enable, "Enable WaveFront soundcard."); | ||
59 | #ifdef CONFIG_PNP | ||
60 | module_param_array(isapnp, bool, NULL, 0444); | ||
61 | MODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards."); | ||
62 | #endif | ||
63 | module_param_array(cs4232_pcm_port, long, NULL, 0444); | ||
64 | MODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface."); | ||
65 | module_param_array(cs4232_pcm_irq, int, NULL, 0444); | ||
66 | MODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface."); | ||
67 | module_param_array(dma1, int, NULL, 0444); | ||
68 | MODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface."); | ||
69 | module_param_array(dma2, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface."); | ||
71 | module_param_array(cs4232_mpu_port, long, NULL, 0444); | ||
72 | MODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface."); | ||
73 | module_param_array(cs4232_mpu_irq, int, NULL, 0444); | ||
74 | MODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface."); | ||
75 | module_param_array(ics2115_irq, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115."); | ||
77 | module_param_array(ics2115_port, long, NULL, 0444); | ||
78 | MODULE_PARM_DESC(ics2115_port, "Port # for ICS2115."); | ||
79 | module_param_array(fm_port, long, NULL, 0444); | ||
80 | MODULE_PARM_DESC(fm_port, "FM port #."); | ||
81 | module_param_array(use_cs4232_midi, bool, NULL, 0444); | ||
82 | MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)"); | ||
83 | |||
84 | static snd_card_t *snd_wavefront_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
85 | |||
86 | #ifdef CONFIG_PNP | ||
87 | |||
88 | static struct pnp_card_device_id snd_wavefront_pnpids[] = { | ||
89 | /* Tropez */ | ||
90 | { .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "PnPb006" }, { "CSC0004" } } }, | ||
91 | /* Tropez+ */ | ||
92 | { .id = "CSC7632", .devs = { { "CSC0000" }, { "CSC0010" }, { "PnPb006" }, { "CSC0004" } } }, | ||
93 | { .id = "" } | ||
94 | }; | ||
95 | |||
96 | MODULE_DEVICE_TABLE(pnp_card, snd_wavefront_pnpids); | ||
97 | |||
98 | static int __devinit | ||
99 | snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *card, | ||
100 | const struct pnp_card_device_id *id) | ||
101 | { | ||
102 | struct pnp_dev *pdev; | ||
103 | struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); | ||
104 | int err; | ||
105 | |||
106 | if (!cfg) | ||
107 | return -ENOMEM; | ||
108 | |||
109 | /* Check for each logical device. */ | ||
110 | |||
111 | /* CS4232 chip (aka "windows sound system") is logical device 0 */ | ||
112 | |||
113 | acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
114 | if (acard->wss == NULL) { | ||
115 | kfree(cfg); | ||
116 | return -EBUSY; | ||
117 | } | ||
118 | |||
119 | /* there is a game port at logical device 1, but we ignore it completely */ | ||
120 | |||
121 | /* the control interface is logical device 2, but we ignore it | ||
122 | completely. in fact, nobody even seems to know what it | ||
123 | does. | ||
124 | */ | ||
125 | |||
126 | /* Only configure the CS4232 MIDI interface if its been | ||
127 | specifically requested. It is logical device 3. | ||
128 | */ | ||
129 | |||
130 | if (use_cs4232_midi[dev]) { | ||
131 | acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); | ||
132 | if (acard->mpu == NULL) { | ||
133 | kfree(cfg); | ||
134 | return -EBUSY; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /* The ICS2115 synth is logical device 4 */ | ||
139 | |||
140 | acard->synth = pnp_request_card_device(card, id->devs[3].id, NULL); | ||
141 | if (acard->synth == NULL) { | ||
142 | kfree(cfg); | ||
143 | return -EBUSY; | ||
144 | } | ||
145 | |||
146 | /* PCM/FM initialization */ | ||
147 | |||
148 | pdev = acard->wss; | ||
149 | |||
150 | pnp_init_resource_table(cfg); | ||
151 | |||
152 | /* An interesting note from the Tropez+ FAQ: | ||
153 | |||
154 | Q. [Ports] Why is the base address of the WSS I/O ports off by 4? | ||
155 | |||
156 | A. WSS I/O requires a block of 8 I/O addresses ("ports"). Of these, the first | ||
157 | 4 are used to identify and configure the board. With the advent of PnP, | ||
158 | these first 4 addresses have become obsolete, and software applications | ||
159 | only use the last 4 addresses to control the codec chip. Therefore, the | ||
160 | base address setting "skips past" the 4 unused addresses. | ||
161 | |||
162 | */ | ||
163 | |||
164 | if (cs4232_pcm_port[dev] != SNDRV_AUTO_PORT) | ||
165 | pnp_resource_change(&cfg->port_resource[0], cs4232_pcm_port[dev], 4); | ||
166 | if (fm_port[dev] != SNDRV_AUTO_PORT) | ||
167 | pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); | ||
168 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
169 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
170 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
171 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
172 | if (cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ) | ||
173 | pnp_resource_change(&cfg->irq_resource[0], cs4232_pcm_irq[dev], 1); | ||
174 | |||
175 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
176 | snd_printk(KERN_ERR "PnP WSS the requested resources are invalid, using auto config\n"); | ||
177 | err = pnp_activate_dev(pdev); | ||
178 | if (err < 0) { | ||
179 | snd_printk(KERN_ERR "PnP WSS pnp configure failure\n"); | ||
180 | kfree(cfg); | ||
181 | return err; | ||
182 | } | ||
183 | |||
184 | cs4232_pcm_port[dev] = pnp_port_start(pdev, 0); | ||
185 | fm_port[dev] = pnp_port_start(pdev, 1); | ||
186 | dma1[dev] = pnp_dma(pdev, 0); | ||
187 | dma2[dev] = pnp_dma(pdev, 1); | ||
188 | cs4232_pcm_irq[dev] = pnp_irq(pdev, 0); | ||
189 | |||
190 | /* Synth initialization */ | ||
191 | |||
192 | pdev = acard->synth; | ||
193 | |||
194 | pnp_init_resource_table(cfg); | ||
195 | |||
196 | if (ics2115_port[dev] != SNDRV_AUTO_PORT) { | ||
197 | pnp_resource_change(&cfg->port_resource[0], ics2115_port[dev], 16); | ||
198 | } | ||
199 | |||
200 | if (ics2115_port[dev] != SNDRV_AUTO_IRQ) { | ||
201 | pnp_resource_change(&cfg->irq_resource[0], ics2115_irq[dev], 1); | ||
202 | } | ||
203 | |||
204 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
205 | snd_printk(KERN_ERR "PnP ICS2115 the requested resources are invalid, using auto config\n"); | ||
206 | err = pnp_activate_dev(pdev); | ||
207 | if (err < 0) { | ||
208 | snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n"); | ||
209 | kfree(cfg); | ||
210 | return err; | ||
211 | } | ||
212 | |||
213 | ics2115_port[dev] = pnp_port_start(pdev, 0); | ||
214 | ics2115_irq[dev] = pnp_irq(pdev, 0); | ||
215 | |||
216 | /* CS4232 MPU initialization. Configure this only if | ||
217 | explicitly requested, since its physically inaccessible and | ||
218 | consumes another IRQ. | ||
219 | */ | ||
220 | |||
221 | if (use_cs4232_midi[dev]) { | ||
222 | |||
223 | pdev = acard->mpu; | ||
224 | |||
225 | pnp_init_resource_table(cfg); | ||
226 | |||
227 | if (cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) | ||
228 | pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_port[dev], 2); | ||
229 | if (cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ) | ||
230 | pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_irq[dev], 1); | ||
231 | |||
232 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
233 | snd_printk(KERN_ERR "PnP MPU401 the requested resources are invalid, using auto config\n"); | ||
234 | err = pnp_activate_dev(pdev); | ||
235 | if (err < 0) { | ||
236 | snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n"); | ||
237 | cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; | ||
238 | } else { | ||
239 | cs4232_mpu_port[dev] = pnp_port_start(pdev, 0); | ||
240 | cs4232_mpu_irq[dev] = pnp_irq(pdev, 0); | ||
241 | } | ||
242 | |||
243 | snd_printk ("CS4232 MPU: port=0x%lx, irq=%i\n", | ||
244 | cs4232_mpu_port[dev], | ||
245 | cs4232_mpu_irq[dev]); | ||
246 | } | ||
247 | |||
248 | snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n", | ||
249 | cs4232_pcm_port[dev], | ||
250 | fm_port[dev], | ||
251 | dma1[dev], | ||
252 | dma2[dev], | ||
253 | cs4232_pcm_irq[dev], | ||
254 | ics2115_port[dev], | ||
255 | ics2115_irq[dev]); | ||
256 | |||
257 | kfree(cfg); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | #endif /* CONFIG_PNP */ | ||
262 | |||
263 | static irqreturn_t snd_wavefront_ics2115_interrupt(int irq, | ||
264 | void *dev_id, | ||
265 | struct pt_regs *regs) | ||
266 | { | ||
267 | snd_wavefront_card_t *acard; | ||
268 | |||
269 | acard = (snd_wavefront_card_t *) dev_id; | ||
270 | |||
271 | if (acard == NULL) | ||
272 | return IRQ_NONE; | ||
273 | |||
274 | if (acard->wavefront.interrupts_are_midi) { | ||
275 | snd_wavefront_midi_interrupt (acard); | ||
276 | } else { | ||
277 | snd_wavefront_internal_interrupt (acard); | ||
278 | } | ||
279 | return IRQ_HANDLED; | ||
280 | } | ||
281 | |||
282 | static snd_hwdep_t * __devinit | ||
283 | snd_wavefront_new_synth (snd_card_t *card, | ||
284 | int hw_dev, | ||
285 | snd_wavefront_card_t *acard) | ||
286 | { | ||
287 | snd_hwdep_t *wavefront_synth; | ||
288 | |||
289 | if (snd_wavefront_detect (acard) < 0) { | ||
290 | return NULL; | ||
291 | } | ||
292 | |||
293 | if (snd_wavefront_start (&acard->wavefront) < 0) { | ||
294 | return NULL; | ||
295 | } | ||
296 | |||
297 | if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) | ||
298 | return NULL; | ||
299 | strcpy (wavefront_synth->name, | ||
300 | "WaveFront (ICS2115) wavetable synthesizer"); | ||
301 | wavefront_synth->ops.open = snd_wavefront_synth_open; | ||
302 | wavefront_synth->ops.release = snd_wavefront_synth_release; | ||
303 | wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl; | ||
304 | |||
305 | return wavefront_synth; | ||
306 | } | ||
307 | |||
308 | static snd_hwdep_t * __devinit | ||
309 | snd_wavefront_new_fx (snd_card_t *card, | ||
310 | int hw_dev, | ||
311 | snd_wavefront_card_t *acard, | ||
312 | unsigned long port) | ||
313 | |||
314 | { | ||
315 | snd_hwdep_t *fx_processor; | ||
316 | |||
317 | if (snd_wavefront_fx_start (&acard->wavefront)) { | ||
318 | snd_printk ("cannot initialize YSS225 FX processor"); | ||
319 | return NULL; | ||
320 | } | ||
321 | |||
322 | if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0) | ||
323 | return NULL; | ||
324 | sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port); | ||
325 | fx_processor->ops.open = snd_wavefront_fx_open; | ||
326 | fx_processor->ops.release = snd_wavefront_fx_release; | ||
327 | fx_processor->ops.ioctl = snd_wavefront_fx_ioctl; | ||
328 | |||
329 | return fx_processor; | ||
330 | } | ||
331 | |||
332 | static snd_wavefront_mpu_id internal_id = internal_mpu; | ||
333 | static snd_wavefront_mpu_id external_id = external_mpu; | ||
334 | |||
335 | static snd_rawmidi_t * __devinit | ||
336 | snd_wavefront_new_midi (snd_card_t *card, | ||
337 | int midi_dev, | ||
338 | snd_wavefront_card_t *acard, | ||
339 | unsigned long port, | ||
340 | snd_wavefront_mpu_id mpu) | ||
341 | |||
342 | { | ||
343 | snd_rawmidi_t *rmidi; | ||
344 | static int first = 1; | ||
345 | |||
346 | if (first) { | ||
347 | first = 0; | ||
348 | acard->wavefront.midi.base = port; | ||
349 | if (snd_wavefront_midi_start (acard)) { | ||
350 | snd_printk ("cannot initialize MIDI interface\n"); | ||
351 | return NULL; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0) | ||
356 | return NULL; | ||
357 | |||
358 | if (mpu == internal_mpu) { | ||
359 | strcpy(rmidi->name, "WaveFront MIDI (Internal)"); | ||
360 | rmidi->private_data = &internal_id; | ||
361 | } else { | ||
362 | strcpy(rmidi->name, "WaveFront MIDI (External)"); | ||
363 | rmidi->private_data = &external_id; | ||
364 | } | ||
365 | |||
366 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output); | ||
367 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input); | ||
368 | |||
369 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | | ||
370 | SNDRV_RAWMIDI_INFO_INPUT | | ||
371 | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
372 | |||
373 | return rmidi; | ||
374 | } | ||
375 | |||
376 | static void | ||
377 | snd_wavefront_free(snd_card_t *card) | ||
378 | { | ||
379 | snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data; | ||
380 | |||
381 | if (acard) { | ||
382 | if (acard->wavefront.res_base != NULL) { | ||
383 | release_resource(acard->wavefront.res_base); | ||
384 | kfree_nocheck(acard->wavefront.res_base); | ||
385 | } | ||
386 | if (acard->wavefront.irq > 0) | ||
387 | free_irq(acard->wavefront.irq, (void *)acard); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | static int __devinit | ||
392 | snd_wavefront_probe (int dev, struct pnp_card_link *pcard, | ||
393 | const struct pnp_card_device_id *pid) | ||
394 | { | ||
395 | snd_card_t *card; | ||
396 | snd_wavefront_card_t *acard; | ||
397 | cs4231_t *chip; | ||
398 | snd_hwdep_t *wavefront_synth; | ||
399 | snd_rawmidi_t *ics2115_internal_rmidi = NULL; | ||
400 | snd_rawmidi_t *ics2115_external_rmidi = NULL; | ||
401 | snd_hwdep_t *fx_processor; | ||
402 | int hw_dev = 0, midi_dev = 0, err; | ||
403 | |||
404 | #ifdef CONFIG_PNP | ||
405 | if (!isapnp[dev]) { | ||
406 | #endif | ||
407 | if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { | ||
408 | snd_printk("specify CS4232 port\n"); | ||
409 | return -EINVAL; | ||
410 | } | ||
411 | if (ics2115_port[dev] == SNDRV_AUTO_PORT) { | ||
412 | snd_printk("specify ICS2115 port\n"); | ||
413 | return -ENODEV; | ||
414 | } | ||
415 | #ifdef CONFIG_PNP | ||
416 | } | ||
417 | #endif | ||
418 | card = snd_card_new (index[dev], | ||
419 | id[dev], | ||
420 | THIS_MODULE, | ||
421 | sizeof(snd_wavefront_card_t)); | ||
422 | |||
423 | if (card == NULL) { | ||
424 | return -ENOMEM; | ||
425 | } | ||
426 | acard = (snd_wavefront_card_t *)card->private_data; | ||
427 | acard->wavefront.irq = -1; | ||
428 | spin_lock_init(&acard->wavefront.irq_lock); | ||
429 | init_waitqueue_head(&acard->wavefront.interrupt_sleeper); | ||
430 | spin_lock_init(&acard->wavefront.midi.open); | ||
431 | spin_lock_init(&acard->wavefront.midi.virtual); | ||
432 | card->private_free = snd_wavefront_free; | ||
433 | |||
434 | #ifdef CONFIG_PNP | ||
435 | if (isapnp[dev]) { | ||
436 | if (snd_wavefront_pnp (dev, acard, pcard, pid) < 0) { | ||
437 | if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { | ||
438 | snd_printk ("isapnp detection failed\n"); | ||
439 | snd_card_free (card); | ||
440 | return -ENODEV; | ||
441 | } | ||
442 | } | ||
443 | snd_card_set_dev(card, &pcard->card->dev); | ||
444 | } | ||
445 | #endif /* CONFIG_PNP */ | ||
446 | |||
447 | /* --------- PCM --------------- */ | ||
448 | |||
449 | if ((err = snd_cs4231_create (card, | ||
450 | cs4232_pcm_port[dev], | ||
451 | -1, | ||
452 | cs4232_pcm_irq[dev], | ||
453 | dma1[dev], | ||
454 | dma2[dev], | ||
455 | CS4231_HW_DETECT, 0, &chip)) < 0) { | ||
456 | snd_card_free(card); | ||
457 | snd_printk ("can't allocate CS4231 device\n"); | ||
458 | return err; | ||
459 | } | ||
460 | |||
461 | if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) { | ||
462 | snd_card_free(card); | ||
463 | return err; | ||
464 | } | ||
465 | if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) { | ||
466 | snd_card_free(card); | ||
467 | return err; | ||
468 | } | ||
469 | |||
470 | /* ---------- OPL3 synth --------- */ | ||
471 | |||
472 | if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { | ||
473 | opl3_t *opl3; | ||
474 | |||
475 | if ((err = snd_opl3_create(card, | ||
476 | fm_port[dev], | ||
477 | fm_port[dev] + 2, | ||
478 | OPL3_HW_OPL3_CS, | ||
479 | 0, &opl3)) < 0) { | ||
480 | snd_printk ("can't allocate or detect OPL3 synth\n"); | ||
481 | snd_card_free(card); | ||
482 | return err; | ||
483 | } | ||
484 | |||
485 | if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) { | ||
486 | snd_card_free(card); | ||
487 | return err; | ||
488 | } | ||
489 | hw_dev++; | ||
490 | } | ||
491 | |||
492 | /* ------- ICS2115 Wavetable synth ------- */ | ||
493 | |||
494 | if ((acard->wavefront.res_base = request_region(ics2115_port[dev], 16, "ICS2115")) == NULL) { | ||
495 | snd_printk("unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", ics2115_port[dev], ics2115_port[dev] + 16 - 1); | ||
496 | snd_card_free(card); | ||
497 | return -EBUSY; | ||
498 | } | ||
499 | if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt, SA_INTERRUPT, "ICS2115", (void *)acard)) { | ||
500 | snd_printk("unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]); | ||
501 | snd_card_free(card); | ||
502 | return -EBUSY; | ||
503 | } | ||
504 | |||
505 | acard->wavefront.irq = ics2115_irq[dev]; | ||
506 | acard->wavefront.base = ics2115_port[dev]; | ||
507 | |||
508 | if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) { | ||
509 | snd_printk ("can't create WaveFront synth device\n"); | ||
510 | snd_card_free(card); | ||
511 | return -ENOMEM; | ||
512 | } | ||
513 | |||
514 | strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); | ||
515 | wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; | ||
516 | hw_dev++; | ||
517 | |||
518 | /* --------- Mixer ------------ */ | ||
519 | |||
520 | if ((err = snd_cs4231_mixer(chip)) < 0) { | ||
521 | snd_printk ("can't allocate mixer device\n"); | ||
522 | snd_card_free(card); | ||
523 | return err; | ||
524 | } | ||
525 | |||
526 | /* -------- CS4232 MPU-401 interface -------- */ | ||
527 | |||
528 | if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
529 | if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, | ||
530 | cs4232_mpu_port[dev], 0, | ||
531 | cs4232_mpu_irq[dev], | ||
532 | SA_INTERRUPT, | ||
533 | NULL)) < 0) { | ||
534 | snd_printk ("can't allocate CS4232 MPU-401 device\n"); | ||
535 | snd_card_free(card); | ||
536 | return err; | ||
537 | } | ||
538 | midi_dev++; | ||
539 | } | ||
540 | |||
541 | /* ------ ICS2115 internal MIDI ------------ */ | ||
542 | |||
543 | if (ics2115_port[dev] > 0 && ics2115_port[dev] != SNDRV_AUTO_PORT) { | ||
544 | ics2115_internal_rmidi = | ||
545 | snd_wavefront_new_midi (card, | ||
546 | midi_dev, | ||
547 | acard, | ||
548 | ics2115_port[dev], | ||
549 | internal_mpu); | ||
550 | if (ics2115_internal_rmidi == NULL) { | ||
551 | snd_printk ("can't setup ICS2115 internal MIDI device\n"); | ||
552 | snd_card_free(card); | ||
553 | return -ENOMEM; | ||
554 | } | ||
555 | midi_dev++; | ||
556 | } | ||
557 | |||
558 | /* ------ ICS2115 external MIDI ------------ */ | ||
559 | |||
560 | if (ics2115_port[dev] > 0 && ics2115_port[dev] != SNDRV_AUTO_PORT) { | ||
561 | ics2115_external_rmidi = | ||
562 | snd_wavefront_new_midi (card, | ||
563 | midi_dev, | ||
564 | acard, | ||
565 | ics2115_port[dev], | ||
566 | external_mpu); | ||
567 | if (ics2115_external_rmidi == NULL) { | ||
568 | snd_printk ("can't setup ICS2115 external MIDI device\n"); | ||
569 | snd_card_free(card); | ||
570 | return -ENOMEM; | ||
571 | } | ||
572 | midi_dev++; | ||
573 | } | ||
574 | |||
575 | /* FX processor for Tropez+ */ | ||
576 | |||
577 | if (acard->wavefront.has_fx) { | ||
578 | fx_processor = snd_wavefront_new_fx (card, | ||
579 | hw_dev, | ||
580 | acard, | ||
581 | ics2115_port[dev]); | ||
582 | if (fx_processor == NULL) { | ||
583 | snd_printk ("can't setup FX device\n"); | ||
584 | snd_card_free(card); | ||
585 | return -ENOMEM; | ||
586 | } | ||
587 | |||
588 | hw_dev++; | ||
589 | |||
590 | strcpy(card->driver, "Tropez+"); | ||
591 | strcpy(card->shortname, "Turtle Beach Tropez+"); | ||
592 | } else { | ||
593 | /* Need a way to distinguish between Maui and Tropez */ | ||
594 | strcpy(card->driver, "WaveFront"); | ||
595 | strcpy(card->shortname, "Turtle Beach WaveFront"); | ||
596 | } | ||
597 | |||
598 | /* ----- Register the card --------- */ | ||
599 | |||
600 | /* Not safe to include "Turtle Beach" in longname, due to | ||
601 | length restrictions | ||
602 | */ | ||
603 | |||
604 | sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d", | ||
605 | card->driver, | ||
606 | chip->port, | ||
607 | cs4232_pcm_irq[dev], | ||
608 | dma1[dev]); | ||
609 | |||
610 | if (dma2[dev] >= 0 && dma2[dev] < 8) | ||
611 | sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); | ||
612 | |||
613 | if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
614 | sprintf (card->longname + strlen (card->longname), | ||
615 | " MPU-401 0x%lx irq %d", | ||
616 | cs4232_mpu_port[dev], | ||
617 | cs4232_mpu_irq[dev]); | ||
618 | } | ||
619 | |||
620 | sprintf (card->longname + strlen (card->longname), | ||
621 | " SYNTH 0x%lx irq %d", | ||
622 | ics2115_port[dev], | ||
623 | ics2115_irq[dev]); | ||
624 | |||
625 | if ((err = snd_card_register(card)) < 0) { | ||
626 | snd_card_free(card); | ||
627 | return err; | ||
628 | } | ||
629 | if (pcard) | ||
630 | pnp_set_card_drvdata(pcard, card); | ||
631 | else | ||
632 | snd_wavefront_legacy[dev] = card; | ||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | #ifdef CONFIG_PNP | ||
637 | |||
638 | static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *card, | ||
639 | const struct pnp_card_device_id *id) | ||
640 | { | ||
641 | static int dev; | ||
642 | int res; | ||
643 | |||
644 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
645 | if (!enable[dev] || !isapnp[dev]) | ||
646 | continue; | ||
647 | res = snd_wavefront_probe(dev, card, id); | ||
648 | if (res < 0) | ||
649 | return res; | ||
650 | dev++; | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | return -ENODEV; | ||
655 | } | ||
656 | |||
657 | static void __devexit snd_wavefront_pnp_remove(struct pnp_card_link * pcard) | ||
658 | { | ||
659 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
660 | |||
661 | snd_card_disconnect(card); | ||
662 | snd_card_free_in_thread(card); | ||
663 | } | ||
664 | |||
665 | static struct pnp_card_driver wavefront_pnpc_driver = { | ||
666 | .flags = PNP_DRIVER_RES_DISABLE, | ||
667 | .name = "wavefront", | ||
668 | .id_table = snd_wavefront_pnpids, | ||
669 | .probe = snd_wavefront_pnp_detect, | ||
670 | .remove = __devexit_p(snd_wavefront_pnp_remove), | ||
671 | }; | ||
672 | |||
673 | #endif /* CONFIG_PNP */ | ||
674 | |||
675 | static int __init alsa_card_wavefront_init(void) | ||
676 | { | ||
677 | int cards = 0; | ||
678 | int dev; | ||
679 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
680 | if (!enable[dev]) | ||
681 | continue; | ||
682 | #ifdef CONFIG_PNP | ||
683 | if (isapnp[dev]) | ||
684 | continue; | ||
685 | #endif | ||
686 | if (snd_wavefront_probe(dev, NULL, NULL) >= 0) | ||
687 | cards++; | ||
688 | } | ||
689 | #ifdef CONFIG_PNP | ||
690 | cards += pnp_register_card_driver(&wavefront_pnpc_driver); | ||
691 | #endif | ||
692 | if (!cards) { | ||
693 | #ifdef CONFIG_PNP | ||
694 | pnp_unregister_card_driver(&wavefront_pnpc_driver); | ||
695 | #endif | ||
696 | #ifdef MODULE | ||
697 | printk (KERN_ERR "No WaveFront cards found or devices busy\n"); | ||
698 | #endif | ||
699 | return -ENODEV; | ||
700 | } | ||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static void __exit alsa_card_wavefront_exit(void) | ||
705 | { | ||
706 | int idx; | ||
707 | |||
708 | #ifdef CONFIG_PNP | ||
709 | pnp_unregister_card_driver(&wavefront_pnpc_driver); | ||
710 | #endif | ||
711 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
712 | snd_card_free(snd_wavefront_legacy[idx]); | ||
713 | } | ||
714 | |||
715 | module_init(alsa_card_wavefront_init) | ||
716 | module_exit(alsa_card_wavefront_exit) | ||
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c new file mode 100644 index 000000000000..0e13623f69f0 --- /dev/null +++ b/sound/isa/wavefront/wavefront_fx.c | |||
@@ -0,0 +1,1019 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1998-2002 by Paul Davis <pbd@op.net> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | #include <sound/driver.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/time.h> | ||
23 | #include <linux/wait.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/snd_wavefront.h> | ||
26 | #include <sound/initval.h> | ||
27 | |||
28 | /* Control bits for the Load Control Register | ||
29 | */ | ||
30 | |||
31 | #define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ | ||
32 | #define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ | ||
33 | #define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ | ||
34 | |||
35 | /* weird stuff, derived from port I/O tracing with dosemu */ | ||
36 | |||
37 | unsigned char page_zero[] __initdata = { | ||
38 | 0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, | ||
39 | 0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, | ||
40 | 0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, | ||
41 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, | ||
50 | 0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, | ||
51 | 0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, | ||
52 | 0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, | ||
53 | 0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, | ||
54 | 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, | ||
55 | 0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, | ||
56 | 0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, | ||
57 | 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, | ||
58 | 0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, | ||
59 | 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, | ||
60 | 0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, | ||
61 | 0x1d, 0x02, 0xdf | ||
62 | }; | ||
63 | |||
64 | unsigned char page_one[] __initdata = { | ||
65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, | ||
66 | 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, | ||
67 | 0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, | ||
68 | 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, | ||
77 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, | ||
78 | 0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, | ||
79 | 0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, | ||
80 | 0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, | ||
81 | 0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, | ||
82 | 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, | ||
83 | 0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, | ||
84 | 0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
85 | 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, | ||
86 | 0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, | ||
87 | 0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, | ||
88 | 0x60, 0x00, 0x1b | ||
89 | }; | ||
90 | |||
91 | unsigned char page_two[] __initdata = { | ||
92 | 0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, | ||
93 | 0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, | ||
94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, | ||
98 | 0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, | ||
99 | 0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, | ||
100 | 0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, | ||
101 | 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, | ||
102 | 0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, | ||
103 | 0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 | ||
104 | }; | ||
105 | |||
106 | unsigned char page_three[] __initdata = { | ||
107 | 0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, | ||
108 | 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
111 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, | ||
113 | 0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, | ||
114 | 0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, | ||
115 | 0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, | ||
116 | 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, | ||
117 | 0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, | ||
118 | 0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 | ||
119 | }; | ||
120 | |||
121 | unsigned char page_four[] __initdata = { | ||
122 | 0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, | ||
123 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
126 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | ||
128 | 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, | ||
129 | 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, | ||
130 | 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, | ||
131 | 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, | ||
132 | 0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, | ||
133 | 0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 | ||
134 | }; | ||
135 | |||
136 | unsigned char page_six[] __initdata = { | ||
137 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, | ||
138 | 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, | ||
139 | 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, | ||
140 | 0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, | ||
141 | 0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, | ||
142 | 0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, | ||
143 | 0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, | ||
144 | 0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, | ||
145 | 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, | ||
146 | 0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, | ||
147 | 0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, | ||
148 | 0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, | ||
149 | 0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, | ||
150 | 0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, | ||
151 | 0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, | ||
152 | 0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, | ||
153 | 0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, | ||
154 | 0x80, 0x00, 0x7e, 0x80, 0x80 | ||
155 | }; | ||
156 | |||
157 | unsigned char page_seven[] __initdata = { | ||
158 | 0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, | ||
159 | 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, | ||
160 | 0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, | ||
161 | 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
163 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
164 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
165 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
167 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
169 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
170 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
171 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, | ||
172 | 0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, | ||
173 | 0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, | ||
174 | 0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, | ||
175 | 0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, | ||
176 | 0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, | ||
177 | 0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, | ||
178 | 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, | ||
179 | 0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, | ||
180 | 0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
181 | 0x00, 0x02, 0x00 | ||
182 | }; | ||
183 | |||
184 | unsigned char page_zero_v2[] __initdata = { | ||
185 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
186 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
187 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
188 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
189 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
190 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
191 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
192 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
193 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
194 | }; | ||
195 | |||
196 | unsigned char page_one_v2[] __initdata = { | ||
197 | 0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
198 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
199 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
200 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
201 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
202 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
204 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
205 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
206 | }; | ||
207 | |||
208 | unsigned char page_two_v2[] __initdata = { | ||
209 | 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
210 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
211 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
212 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
213 | 0x00, 0x00, 0x00, 0x00 | ||
214 | }; | ||
215 | unsigned char page_three_v2[] __initdata = { | ||
216 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
217 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
218 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
219 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
220 | 0x00, 0x00, 0x00, 0x00 | ||
221 | }; | ||
222 | unsigned char page_four_v2[] __initdata = { | ||
223 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
224 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
225 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
226 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
227 | 0x00, 0x00, 0x00, 0x00 | ||
228 | }; | ||
229 | |||
230 | unsigned char page_seven_v2[] __initdata = { | ||
231 | 0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
232 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
233 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
234 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
235 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
236 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
237 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
238 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
239 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
240 | }; | ||
241 | |||
242 | unsigned char mod_v2[] __initdata = { | ||
243 | 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, | ||
244 | 0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, | ||
245 | 0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, | ||
246 | 0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, | ||
247 | 0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, | ||
248 | 0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, | ||
249 | 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, | ||
250 | 0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, | ||
251 | 0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, | ||
252 | 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, | ||
253 | 0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, | ||
254 | 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, | ||
255 | 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, | ||
256 | 0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, | ||
257 | 0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, | ||
258 | 0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, | ||
259 | 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, | ||
260 | 0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, | ||
261 | 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, | ||
262 | 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, | ||
263 | 0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, | ||
264 | 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, | ||
265 | 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, | ||
266 | 0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, | ||
267 | 0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, | ||
268 | 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, | ||
269 | 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, | ||
270 | 0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 | ||
271 | }; | ||
272 | unsigned char coefficients[] __initdata = { | ||
273 | 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, | ||
274 | 0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, | ||
275 | 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, | ||
276 | 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, | ||
277 | 0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, | ||
278 | 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, | ||
279 | 0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, | ||
280 | 0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, | ||
281 | 0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, | ||
282 | 0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, | ||
283 | 0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, | ||
284 | 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, | ||
285 | 0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, | ||
286 | 0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, | ||
287 | 0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, | ||
288 | 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, | ||
289 | 0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, | ||
290 | 0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, | ||
291 | 0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, | ||
292 | 0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, | ||
293 | 0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, | ||
294 | 0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, | ||
295 | 0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, | ||
296 | 0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, | ||
297 | 0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, | ||
298 | 0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, | ||
299 | 0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, | ||
300 | 0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, | ||
301 | 0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, | ||
302 | 0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, | ||
303 | 0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, | ||
304 | 0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, | ||
305 | 0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, | ||
306 | 0xba | ||
307 | }; | ||
308 | unsigned char coefficients2[] __initdata = { | ||
309 | 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, | ||
310 | 0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, | ||
311 | 0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, | ||
312 | 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, | ||
313 | 0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 | ||
314 | }; | ||
315 | unsigned char coefficients3[] __initdata = { | ||
316 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, | ||
317 | 0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, | ||
318 | 0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, | ||
319 | 0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, | ||
320 | 0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, | ||
321 | 0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, | ||
322 | 0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, | ||
323 | 0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, | ||
324 | 0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, | ||
325 | 0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, | ||
326 | 0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, | ||
327 | 0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, | ||
328 | 0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, | ||
329 | 0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, | ||
330 | 0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, | ||
331 | 0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, | ||
332 | 0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, | ||
333 | 0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, | ||
334 | 0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, | ||
335 | 0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, | ||
336 | 0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, | ||
337 | 0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, | ||
338 | 0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, | ||
339 | 0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, | ||
340 | 0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, | ||
341 | 0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, | ||
342 | 0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, | ||
343 | 0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, | ||
344 | 0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, | ||
345 | 0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, | ||
346 | 0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, | ||
347 | 0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, | ||
348 | 0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, | ||
349 | 0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, | ||
350 | 0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, | ||
351 | 0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, | ||
352 | 0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff | ||
353 | }; | ||
354 | |||
355 | static int | ||
356 | wavefront_fx_idle (snd_wavefront_t *dev) | ||
357 | |||
358 | { | ||
359 | int i; | ||
360 | unsigned int x = 0x80; | ||
361 | |||
362 | for (i = 0; i < 1000; i++) { | ||
363 | x = inb (dev->fx_status); | ||
364 | if ((x & 0x80) == 0) { | ||
365 | break; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | if (x & 0x80) { | ||
370 | snd_printk ("FX device never idle.\n"); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | return (1); | ||
375 | } | ||
376 | |||
377 | static void | ||
378 | wavefront_fx_mute (snd_wavefront_t *dev, int onoff) | ||
379 | |||
380 | { | ||
381 | if (!wavefront_fx_idle(dev)) { | ||
382 | return; | ||
383 | } | ||
384 | |||
385 | outb (onoff ? 0x02 : 0x00, dev->fx_op); | ||
386 | } | ||
387 | |||
388 | static int | ||
389 | wavefront_fx_memset (snd_wavefront_t *dev, | ||
390 | int page, | ||
391 | int addr, | ||
392 | int cnt, | ||
393 | unsigned short *data) | ||
394 | { | ||
395 | if (page < 0 || page > 7) { | ||
396 | snd_printk ("FX memset: " | ||
397 | "page must be >= 0 and <= 7\n"); | ||
398 | return -(EINVAL); | ||
399 | } | ||
400 | |||
401 | if (addr < 0 || addr > 0x7f) { | ||
402 | snd_printk ("FX memset: " | ||
403 | "addr must be >= 0 and <= 7f\n"); | ||
404 | return -(EINVAL); | ||
405 | } | ||
406 | |||
407 | if (cnt == 1) { | ||
408 | |||
409 | outb (FX_LSB_TRANSFER, dev->fx_lcr); | ||
410 | outb (page, dev->fx_dsp_page); | ||
411 | outb (addr, dev->fx_dsp_addr); | ||
412 | outb ((data[0] >> 8), dev->fx_dsp_msb); | ||
413 | outb ((data[0] & 0xff), dev->fx_dsp_lsb); | ||
414 | |||
415 | snd_printk ("FX: addr %d:%x set to 0x%x\n", | ||
416 | page, addr, data[0]); | ||
417 | |||
418 | } else { | ||
419 | int i; | ||
420 | |||
421 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
422 | outb (page, dev->fx_dsp_page); | ||
423 | outb (addr, dev->fx_dsp_addr); | ||
424 | |||
425 | for (i = 0; i < cnt; i++) { | ||
426 | outb ((data[i] >> 8), dev->fx_dsp_msb); | ||
427 | outb ((data[i] & 0xff), dev->fx_dsp_lsb); | ||
428 | if (!wavefront_fx_idle (dev)) { | ||
429 | break; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | if (i != cnt) { | ||
434 | snd_printk ("FX memset " | ||
435 | "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", | ||
436 | page, addr, (unsigned long) data, cnt); | ||
437 | return -(EIO); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | int | ||
445 | snd_wavefront_fx_detect (snd_wavefront_t *dev) | ||
446 | |||
447 | { | ||
448 | /* This is a crude check, but its the best one I have for now. | ||
449 | Certainly on the Maui and the Tropez, wavefront_fx_idle() will | ||
450 | report "never idle", which suggests that this test should | ||
451 | work OK. | ||
452 | */ | ||
453 | |||
454 | if (inb (dev->fx_status) & 0x80) { | ||
455 | snd_printk ("Hmm, probably a Maui or Tropez.\n"); | ||
456 | return -1; | ||
457 | } | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | int | ||
463 | snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file) | ||
464 | |||
465 | { | ||
466 | if (!try_module_get(hw->card->module)) | ||
467 | return -EFAULT; | ||
468 | file->private_data = hw; | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | int | ||
473 | snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file) | ||
474 | |||
475 | { | ||
476 | module_put(hw->card->module); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | int | ||
481 | snd_wavefront_fx_ioctl (snd_hwdep_t *sdev, struct file *file, | ||
482 | unsigned int cmd, unsigned long arg) | ||
483 | |||
484 | { | ||
485 | snd_card_t *card; | ||
486 | snd_wavefront_card_t *acard; | ||
487 | snd_wavefront_t *dev; | ||
488 | wavefront_fx_info r; | ||
489 | unsigned short *page_data = NULL; | ||
490 | unsigned short *pd; | ||
491 | int err = 0; | ||
492 | |||
493 | snd_assert(sdev->card != NULL, return -ENODEV); | ||
494 | |||
495 | card = sdev->card; | ||
496 | |||
497 | snd_assert(card->private_data != NULL, return -ENODEV); | ||
498 | |||
499 | acard = card->private_data; | ||
500 | dev = &acard->wavefront; | ||
501 | |||
502 | if (copy_from_user (&r, (void __user *)arg, sizeof (wavefront_fx_info))) | ||
503 | return -EFAULT; | ||
504 | |||
505 | switch (r.request) { | ||
506 | case WFFX_MUTE: | ||
507 | wavefront_fx_mute (dev, r.data[0]); | ||
508 | return -EIO; | ||
509 | |||
510 | case WFFX_MEMSET: | ||
511 | if (r.data[2] <= 0) { | ||
512 | snd_printk ("cannot write " | ||
513 | "<= 0 bytes to FX\n"); | ||
514 | return -EIO; | ||
515 | } else if (r.data[2] == 1) { | ||
516 | pd = (unsigned short *) &r.data[3]; | ||
517 | } else { | ||
518 | if (r.data[2] > 256) { | ||
519 | snd_printk ("cannot write " | ||
520 | "> 512 bytes to FX\n"); | ||
521 | return -EIO; | ||
522 | } | ||
523 | page_data = kmalloc(r.data[2] * sizeof(short), GFP_KERNEL); | ||
524 | if (!page_data) | ||
525 | return -ENOMEM; | ||
526 | if (copy_from_user (page_data, | ||
527 | (unsigned char __user *) r.data[3], | ||
528 | r.data[2] * sizeof(short))) { | ||
529 | kfree(page_data); | ||
530 | return -EFAULT; | ||
531 | } | ||
532 | pd = page_data; | ||
533 | } | ||
534 | |||
535 | err = wavefront_fx_memset (dev, | ||
536 | r.data[0], /* page */ | ||
537 | r.data[1], /* addr */ | ||
538 | r.data[2], /* cnt */ | ||
539 | pd); | ||
540 | kfree(page_data); | ||
541 | break; | ||
542 | |||
543 | default: | ||
544 | snd_printk ("FX: ioctl %d not yet supported\n", | ||
545 | r.request); | ||
546 | return -ENOTTY; | ||
547 | } | ||
548 | return err; | ||
549 | } | ||
550 | |||
551 | /* YSS225 initialization. | ||
552 | |||
553 | This code was developed using DOSEMU. The Turtle Beach SETUPSND | ||
554 | utility was run with I/O tracing in DOSEMU enabled, and a reconstruction | ||
555 | of the port I/O done, using the Yamaha faxback document as a guide | ||
556 | to add more logic to the code. Its really pretty weird. | ||
557 | |||
558 | There was an alternative approach of just dumping the whole I/O | ||
559 | sequence as a series of port/value pairs and a simple loop | ||
560 | that output it. However, I hope that eventually I'll get more | ||
561 | control over what this code does, and so I tried to stick with | ||
562 | a somewhat "algorithmic" approach. | ||
563 | */ | ||
564 | |||
565 | |||
566 | int __init | ||
567 | snd_wavefront_fx_start (snd_wavefront_t *dev) | ||
568 | |||
569 | { | ||
570 | unsigned int i, j; | ||
571 | |||
572 | /* Set all bits for all channels on the MOD unit to zero */ | ||
573 | /* XXX But why do this twice ? */ | ||
574 | |||
575 | for (j = 0; j < 2; j++) { | ||
576 | for (i = 0x10; i <= 0xff; i++) { | ||
577 | |||
578 | if (!wavefront_fx_idle (dev)) { | ||
579 | return (-1); | ||
580 | } | ||
581 | |||
582 | outb (i, dev->fx_mod_addr); | ||
583 | outb (0x0, dev->fx_mod_data); | ||
584 | } | ||
585 | } | ||
586 | |||
587 | if (!wavefront_fx_idle (dev)) return (-1); | ||
588 | outb (0x02, dev->fx_op); /* mute on */ | ||
589 | |||
590 | if (!wavefront_fx_idle (dev)) return (-1); | ||
591 | outb (0x07, dev->fx_dsp_page); | ||
592 | outb (0x44, dev->fx_dsp_addr); | ||
593 | outb (0x00, dev->fx_dsp_msb); | ||
594 | outb (0x00, dev->fx_dsp_lsb); | ||
595 | if (!wavefront_fx_idle (dev)) return (-1); | ||
596 | outb (0x07, dev->fx_dsp_page); | ||
597 | outb (0x42, dev->fx_dsp_addr); | ||
598 | outb (0x00, dev->fx_dsp_msb); | ||
599 | outb (0x00, dev->fx_dsp_lsb); | ||
600 | if (!wavefront_fx_idle (dev)) return (-1); | ||
601 | outb (0x07, dev->fx_dsp_page); | ||
602 | outb (0x43, dev->fx_dsp_addr); | ||
603 | outb (0x00, dev->fx_dsp_msb); | ||
604 | outb (0x00, dev->fx_dsp_lsb); | ||
605 | if (!wavefront_fx_idle (dev)) return (-1); | ||
606 | outb (0x07, dev->fx_dsp_page); | ||
607 | outb (0x7c, dev->fx_dsp_addr); | ||
608 | outb (0x00, dev->fx_dsp_msb); | ||
609 | outb (0x00, dev->fx_dsp_lsb); | ||
610 | if (!wavefront_fx_idle (dev)) return (-1); | ||
611 | outb (0x07, dev->fx_dsp_page); | ||
612 | outb (0x7e, dev->fx_dsp_addr); | ||
613 | outb (0x00, dev->fx_dsp_msb); | ||
614 | outb (0x00, dev->fx_dsp_lsb); | ||
615 | if (!wavefront_fx_idle (dev)) return (-1); | ||
616 | outb (0x07, dev->fx_dsp_page); | ||
617 | outb (0x46, dev->fx_dsp_addr); | ||
618 | outb (0x00, dev->fx_dsp_msb); | ||
619 | outb (0x00, dev->fx_dsp_lsb); | ||
620 | if (!wavefront_fx_idle (dev)) return (-1); | ||
621 | outb (0x07, dev->fx_dsp_page); | ||
622 | outb (0x49, dev->fx_dsp_addr); | ||
623 | outb (0x00, dev->fx_dsp_msb); | ||
624 | outb (0x00, dev->fx_dsp_lsb); | ||
625 | if (!wavefront_fx_idle (dev)) return (-1); | ||
626 | outb (0x07, dev->fx_dsp_page); | ||
627 | outb (0x47, dev->fx_dsp_addr); | ||
628 | outb (0x00, dev->fx_dsp_msb); | ||
629 | outb (0x00, dev->fx_dsp_lsb); | ||
630 | if (!wavefront_fx_idle (dev)) return (-1); | ||
631 | outb (0x07, dev->fx_dsp_page); | ||
632 | outb (0x4a, dev->fx_dsp_addr); | ||
633 | outb (0x00, dev->fx_dsp_msb); | ||
634 | outb (0x00, dev->fx_dsp_lsb); | ||
635 | |||
636 | /* either because of stupidity by TB's programmers, or because it | ||
637 | actually does something, rezero the MOD page. | ||
638 | */ | ||
639 | for (i = 0x10; i <= 0xff; i++) { | ||
640 | |||
641 | if (!wavefront_fx_idle (dev)) { | ||
642 | return (-1); | ||
643 | } | ||
644 | |||
645 | outb (i, dev->fx_mod_addr); | ||
646 | outb (0x0, dev->fx_mod_data); | ||
647 | } | ||
648 | /* load page zero */ | ||
649 | |||
650 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
651 | outb (0x00, dev->fx_dsp_page); | ||
652 | outb (0x00, dev->fx_dsp_addr); | ||
653 | |||
654 | for (i = 0; i < sizeof (page_zero); i += 2) { | ||
655 | outb (page_zero[i], dev->fx_dsp_msb); | ||
656 | outb (page_zero[i+1], dev->fx_dsp_lsb); | ||
657 | if (!wavefront_fx_idle (dev)) return (-1); | ||
658 | } | ||
659 | |||
660 | /* Now load page one */ | ||
661 | |||
662 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
663 | outb (0x01, dev->fx_dsp_page); | ||
664 | outb (0x00, dev->fx_dsp_addr); | ||
665 | |||
666 | for (i = 0; i < sizeof (page_one); i += 2) { | ||
667 | outb (page_one[i], dev->fx_dsp_msb); | ||
668 | outb (page_one[i+1], dev->fx_dsp_lsb); | ||
669 | if (!wavefront_fx_idle (dev)) return (-1); | ||
670 | } | ||
671 | |||
672 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
673 | outb (0x02, dev->fx_dsp_page); | ||
674 | outb (0x00, dev->fx_dsp_addr); | ||
675 | |||
676 | for (i = 0; i < sizeof (page_two); i++) { | ||
677 | outb (page_two[i], dev->fx_dsp_lsb); | ||
678 | if (!wavefront_fx_idle (dev)) return (-1); | ||
679 | } | ||
680 | |||
681 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
682 | outb (0x03, dev->fx_dsp_page); | ||
683 | outb (0x00, dev->fx_dsp_addr); | ||
684 | |||
685 | for (i = 0; i < sizeof (page_three); i++) { | ||
686 | outb (page_three[i], dev->fx_dsp_lsb); | ||
687 | if (!wavefront_fx_idle (dev)) return (-1); | ||
688 | } | ||
689 | |||
690 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
691 | outb (0x04, dev->fx_dsp_page); | ||
692 | outb (0x00, dev->fx_dsp_addr); | ||
693 | |||
694 | for (i = 0; i < sizeof (page_four); i++) { | ||
695 | outb (page_four[i], dev->fx_dsp_lsb); | ||
696 | if (!wavefront_fx_idle (dev)) return (-1); | ||
697 | } | ||
698 | |||
699 | /* Load memory area (page six) */ | ||
700 | |||
701 | outb (FX_LSB_TRANSFER, dev->fx_lcr); | ||
702 | outb (0x06, dev->fx_dsp_page); | ||
703 | |||
704 | for (i = 0; i < sizeof (page_six); i += 3) { | ||
705 | outb (page_six[i], dev->fx_dsp_addr); | ||
706 | outb (page_six[i+1], dev->fx_dsp_msb); | ||
707 | outb (page_six[i+2], dev->fx_dsp_lsb); | ||
708 | if (!wavefront_fx_idle (dev)) return (-1); | ||
709 | } | ||
710 | |||
711 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
712 | outb (0x07, dev->fx_dsp_page); | ||
713 | outb (0x00, dev->fx_dsp_addr); | ||
714 | |||
715 | for (i = 0; i < sizeof (page_seven); i += 2) { | ||
716 | outb (page_seven[i], dev->fx_dsp_msb); | ||
717 | outb (page_seven[i+1], dev->fx_dsp_lsb); | ||
718 | if (!wavefront_fx_idle (dev)) return (-1); | ||
719 | } | ||
720 | |||
721 | /* Now setup the MOD area. We do this algorithmically in order to | ||
722 | save a little data space. It could be done in the same fashion | ||
723 | as the "pages". | ||
724 | */ | ||
725 | |||
726 | for (i = 0x00; i <= 0x0f; i++) { | ||
727 | outb (0x01, dev->fx_mod_addr); | ||
728 | outb (i, dev->fx_mod_data); | ||
729 | if (!wavefront_fx_idle (dev)) return (-1); | ||
730 | outb (0x02, dev->fx_mod_addr); | ||
731 | outb (0x00, dev->fx_mod_data); | ||
732 | if (!wavefront_fx_idle (dev)) return (-1); | ||
733 | } | ||
734 | |||
735 | for (i = 0xb0; i <= 0xbf; i++) { | ||
736 | outb (i, dev->fx_mod_addr); | ||
737 | outb (0x20, dev->fx_mod_data); | ||
738 | if (!wavefront_fx_idle (dev)) return (-1); | ||
739 | } | ||
740 | |||
741 | for (i = 0xf0; i <= 0xff; i++) { | ||
742 | outb (i, dev->fx_mod_addr); | ||
743 | outb (0x20, dev->fx_mod_data); | ||
744 | if (!wavefront_fx_idle (dev)) return (-1); | ||
745 | } | ||
746 | |||
747 | for (i = 0x10; i <= 0x1d; i++) { | ||
748 | outb (i, dev->fx_mod_addr); | ||
749 | outb (0xff, dev->fx_mod_data); | ||
750 | if (!wavefront_fx_idle (dev)) return (-1); | ||
751 | } | ||
752 | |||
753 | outb (0x1e, dev->fx_mod_addr); | ||
754 | outb (0x40, dev->fx_mod_data); | ||
755 | if (!wavefront_fx_idle (dev)) return (-1); | ||
756 | |||
757 | for (i = 0x1f; i <= 0x2d; i++) { | ||
758 | outb (i, dev->fx_mod_addr); | ||
759 | outb (0xff, dev->fx_mod_data); | ||
760 | if (!wavefront_fx_idle (dev)) return (-1); | ||
761 | } | ||
762 | |||
763 | outb (0x2e, dev->fx_mod_addr); | ||
764 | outb (0x00, dev->fx_mod_data); | ||
765 | if (!wavefront_fx_idle (dev)) return (-1); | ||
766 | |||
767 | for (i = 0x2f; i <= 0x3e; i++) { | ||
768 | outb (i, dev->fx_mod_addr); | ||
769 | outb (0x00, dev->fx_mod_data); | ||
770 | if (!wavefront_fx_idle (dev)) return (-1); | ||
771 | } | ||
772 | |||
773 | outb (0x3f, dev->fx_mod_addr); | ||
774 | outb (0x20, dev->fx_mod_data); | ||
775 | if (!wavefront_fx_idle (dev)) return (-1); | ||
776 | |||
777 | for (i = 0x40; i <= 0x4d; i++) { | ||
778 | outb (i, dev->fx_mod_addr); | ||
779 | outb (0x00, dev->fx_mod_data); | ||
780 | if (!wavefront_fx_idle (dev)) return (-1); | ||
781 | } | ||
782 | |||
783 | outb (0x4e, dev->fx_mod_addr); | ||
784 | outb (0x0e, dev->fx_mod_data); | ||
785 | if (!wavefront_fx_idle (dev)) return (-1); | ||
786 | outb (0x4f, dev->fx_mod_addr); | ||
787 | outb (0x0e, dev->fx_mod_data); | ||
788 | if (!wavefront_fx_idle (dev)) return (-1); | ||
789 | |||
790 | |||
791 | for (i = 0x50; i <= 0x6b; i++) { | ||
792 | outb (i, dev->fx_mod_addr); | ||
793 | outb (0x00, dev->fx_mod_data); | ||
794 | if (!wavefront_fx_idle (dev)) return (-1); | ||
795 | } | ||
796 | |||
797 | outb (0x6c, dev->fx_mod_addr); | ||
798 | outb (0x40, dev->fx_mod_data); | ||
799 | if (!wavefront_fx_idle (dev)) return (-1); | ||
800 | |||
801 | outb (0x6d, dev->fx_mod_addr); | ||
802 | outb (0x00, dev->fx_mod_data); | ||
803 | if (!wavefront_fx_idle (dev)) return (-1); | ||
804 | |||
805 | outb (0x6e, dev->fx_mod_addr); | ||
806 | outb (0x40, dev->fx_mod_data); | ||
807 | if (!wavefront_fx_idle (dev)) return (-1); | ||
808 | |||
809 | outb (0x6f, dev->fx_mod_addr); | ||
810 | outb (0x40, dev->fx_mod_data); | ||
811 | if (!wavefront_fx_idle (dev)) return (-1); | ||
812 | |||
813 | for (i = 0x70; i <= 0x7f; i++) { | ||
814 | outb (i, dev->fx_mod_addr); | ||
815 | outb (0xc0, dev->fx_mod_data); | ||
816 | if (!wavefront_fx_idle (dev)) return (-1); | ||
817 | } | ||
818 | |||
819 | for (i = 0x80; i <= 0xaf; i++) { | ||
820 | outb (i, dev->fx_mod_addr); | ||
821 | outb (0x00, dev->fx_mod_data); | ||
822 | if (!wavefront_fx_idle (dev)) return (-1); | ||
823 | } | ||
824 | |||
825 | for (i = 0xc0; i <= 0xdd; i++) { | ||
826 | outb (i, dev->fx_mod_addr); | ||
827 | outb (0x00, dev->fx_mod_data); | ||
828 | if (!wavefront_fx_idle (dev)) return (-1); | ||
829 | } | ||
830 | |||
831 | outb (0xde, dev->fx_mod_addr); | ||
832 | outb (0x10, dev->fx_mod_data); | ||
833 | if (!wavefront_fx_idle (dev)) return (-1); | ||
834 | outb (0xdf, dev->fx_mod_addr); | ||
835 | outb (0x10, dev->fx_mod_data); | ||
836 | if (!wavefront_fx_idle (dev)) return (-1); | ||
837 | |||
838 | for (i = 0xe0; i <= 0xef; i++) { | ||
839 | outb (i, dev->fx_mod_addr); | ||
840 | outb (0x00, dev->fx_mod_data); | ||
841 | if (!wavefront_fx_idle (dev)) return (-1); | ||
842 | } | ||
843 | |||
844 | for (i = 0x00; i <= 0x0f; i++) { | ||
845 | outb (0x01, dev->fx_mod_addr); | ||
846 | outb (i, dev->fx_mod_data); | ||
847 | outb (0x02, dev->fx_mod_addr); | ||
848 | outb (0x01, dev->fx_mod_data); | ||
849 | if (!wavefront_fx_idle (dev)) return (-1); | ||
850 | } | ||
851 | |||
852 | outb (0x02, dev->fx_op); /* mute on */ | ||
853 | |||
854 | /* Now set the coefficients and so forth for the programs above */ | ||
855 | |||
856 | for (i = 0; i < sizeof (coefficients); i += 4) { | ||
857 | outb (coefficients[i], dev->fx_dsp_page); | ||
858 | outb (coefficients[i+1], dev->fx_dsp_addr); | ||
859 | outb (coefficients[i+2], dev->fx_dsp_msb); | ||
860 | outb (coefficients[i+3], dev->fx_dsp_lsb); | ||
861 | if (!wavefront_fx_idle (dev)) return (-1); | ||
862 | } | ||
863 | |||
864 | /* Some settings (?) that are too small to bundle into loops */ | ||
865 | |||
866 | if (!wavefront_fx_idle (dev)) return (-1); | ||
867 | outb (0x1e, dev->fx_mod_addr); | ||
868 | outb (0x14, dev->fx_mod_data); | ||
869 | if (!wavefront_fx_idle (dev)) return (-1); | ||
870 | outb (0xde, dev->fx_mod_addr); | ||
871 | outb (0x20, dev->fx_mod_data); | ||
872 | if (!wavefront_fx_idle (dev)) return (-1); | ||
873 | outb (0xdf, dev->fx_mod_addr); | ||
874 | outb (0x20, dev->fx_mod_data); | ||
875 | |||
876 | /* some more coefficients */ | ||
877 | |||
878 | if (!wavefront_fx_idle (dev)) return (-1); | ||
879 | outb (0x06, dev->fx_dsp_page); | ||
880 | outb (0x78, dev->fx_dsp_addr); | ||
881 | outb (0x00, dev->fx_dsp_msb); | ||
882 | outb (0x40, dev->fx_dsp_lsb); | ||
883 | if (!wavefront_fx_idle (dev)) return (-1); | ||
884 | outb (0x07, dev->fx_dsp_page); | ||
885 | outb (0x03, dev->fx_dsp_addr); | ||
886 | outb (0x0f, dev->fx_dsp_msb); | ||
887 | outb (0xff, dev->fx_dsp_lsb); | ||
888 | if (!wavefront_fx_idle (dev)) return (-1); | ||
889 | outb (0x07, dev->fx_dsp_page); | ||
890 | outb (0x0b, dev->fx_dsp_addr); | ||
891 | outb (0x0f, dev->fx_dsp_msb); | ||
892 | outb (0xff, dev->fx_dsp_lsb); | ||
893 | if (!wavefront_fx_idle (dev)) return (-1); | ||
894 | outb (0x07, dev->fx_dsp_page); | ||
895 | outb (0x02, dev->fx_dsp_addr); | ||
896 | outb (0x00, dev->fx_dsp_msb); | ||
897 | outb (0x00, dev->fx_dsp_lsb); | ||
898 | if (!wavefront_fx_idle (dev)) return (-1); | ||
899 | outb (0x07, dev->fx_dsp_page); | ||
900 | outb (0x0a, dev->fx_dsp_addr); | ||
901 | outb (0x00, dev->fx_dsp_msb); | ||
902 | outb (0x00, dev->fx_dsp_lsb); | ||
903 | if (!wavefront_fx_idle (dev)) return (-1); | ||
904 | outb (0x07, dev->fx_dsp_page); | ||
905 | outb (0x46, dev->fx_dsp_addr); | ||
906 | outb (0x00, dev->fx_dsp_msb); | ||
907 | outb (0x00, dev->fx_dsp_lsb); | ||
908 | if (!wavefront_fx_idle (dev)) return (-1); | ||
909 | outb (0x07, dev->fx_dsp_page); | ||
910 | outb (0x49, dev->fx_dsp_addr); | ||
911 | outb (0x00, dev->fx_dsp_msb); | ||
912 | outb (0x00, dev->fx_dsp_lsb); | ||
913 | |||
914 | /* Now, for some strange reason, lets reload every page | ||
915 | and all the coefficients over again. I have *NO* idea | ||
916 | why this is done. I do know that no sound is produced | ||
917 | is this phase is omitted. | ||
918 | */ | ||
919 | |||
920 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
921 | outb (0x00, dev->fx_dsp_page); | ||
922 | outb (0x10, dev->fx_dsp_addr); | ||
923 | |||
924 | for (i = 0; i < sizeof (page_zero_v2); i += 2) { | ||
925 | outb (page_zero_v2[i], dev->fx_dsp_msb); | ||
926 | outb (page_zero_v2[i+1], dev->fx_dsp_lsb); | ||
927 | if (!wavefront_fx_idle (dev)) return (-1); | ||
928 | } | ||
929 | |||
930 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
931 | outb (0x01, dev->fx_dsp_page); | ||
932 | outb (0x10, dev->fx_dsp_addr); | ||
933 | |||
934 | for (i = 0; i < sizeof (page_one_v2); i += 2) { | ||
935 | outb (page_one_v2[i], dev->fx_dsp_msb); | ||
936 | outb (page_one_v2[i+1], dev->fx_dsp_lsb); | ||
937 | if (!wavefront_fx_idle (dev)) return (-1); | ||
938 | } | ||
939 | |||
940 | if (!wavefront_fx_idle (dev)) return (-1); | ||
941 | if (!wavefront_fx_idle (dev)) return (-1); | ||
942 | |||
943 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
944 | outb (0x02, dev->fx_dsp_page); | ||
945 | outb (0x10, dev->fx_dsp_addr); | ||
946 | |||
947 | for (i = 0; i < sizeof (page_two_v2); i++) { | ||
948 | outb (page_two_v2[i], dev->fx_dsp_lsb); | ||
949 | if (!wavefront_fx_idle (dev)) return (-1); | ||
950 | } | ||
951 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
952 | outb (0x03, dev->fx_dsp_page); | ||
953 | outb (0x10, dev->fx_dsp_addr); | ||
954 | |||
955 | for (i = 0; i < sizeof (page_three_v2); i++) { | ||
956 | outb (page_three_v2[i], dev->fx_dsp_lsb); | ||
957 | if (!wavefront_fx_idle (dev)) return (-1); | ||
958 | } | ||
959 | |||
960 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
961 | outb (0x04, dev->fx_dsp_page); | ||
962 | outb (0x10, dev->fx_dsp_addr); | ||
963 | |||
964 | for (i = 0; i < sizeof (page_four_v2); i++) { | ||
965 | outb (page_four_v2[i], dev->fx_dsp_lsb); | ||
966 | if (!wavefront_fx_idle (dev)) return (-1); | ||
967 | } | ||
968 | |||
969 | outb (FX_LSB_TRANSFER, dev->fx_lcr); | ||
970 | outb (0x06, dev->fx_dsp_page); | ||
971 | |||
972 | /* Page six v.2 is algorithmic */ | ||
973 | |||
974 | for (i = 0x10; i <= 0x3e; i += 2) { | ||
975 | outb (i, dev->fx_dsp_addr); | ||
976 | outb (0x00, dev->fx_dsp_msb); | ||
977 | outb (0x00, dev->fx_dsp_lsb); | ||
978 | if (!wavefront_fx_idle (dev)) return (-1); | ||
979 | } | ||
980 | |||
981 | outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); | ||
982 | outb (0x07, dev->fx_dsp_page); | ||
983 | outb (0x10, dev->fx_dsp_addr); | ||
984 | |||
985 | for (i = 0; i < sizeof (page_seven_v2); i += 2) { | ||
986 | outb (page_seven_v2[i], dev->fx_dsp_msb); | ||
987 | outb (page_seven_v2[i+1], dev->fx_dsp_lsb); | ||
988 | if (!wavefront_fx_idle (dev)) return (-1); | ||
989 | } | ||
990 | |||
991 | for (i = 0x00; i < sizeof(mod_v2); i += 2) { | ||
992 | outb (mod_v2[i], dev->fx_mod_addr); | ||
993 | outb (mod_v2[i+1], dev->fx_mod_data); | ||
994 | if (!wavefront_fx_idle (dev)) return (-1); | ||
995 | } | ||
996 | |||
997 | for (i = 0; i < sizeof (coefficients2); i += 4) { | ||
998 | outb (coefficients2[i], dev->fx_dsp_page); | ||
999 | outb (coefficients2[i+1], dev->fx_dsp_addr); | ||
1000 | outb (coefficients2[i+2], dev->fx_dsp_msb); | ||
1001 | outb (coefficients2[i+3], dev->fx_dsp_lsb); | ||
1002 | if (!wavefront_fx_idle (dev)) return (-1); | ||
1003 | } | ||
1004 | |||
1005 | for (i = 0; i < sizeof (coefficients3); i += 2) { | ||
1006 | int x; | ||
1007 | |||
1008 | outb (0x07, dev->fx_dsp_page); | ||
1009 | x = (i % 4) ? 0x4e : 0x4c; | ||
1010 | outb (x, dev->fx_dsp_addr); | ||
1011 | outb (coefficients3[i], dev->fx_dsp_msb); | ||
1012 | outb (coefficients3[i+1], dev->fx_dsp_lsb); | ||
1013 | } | ||
1014 | |||
1015 | outb (0x00, dev->fx_op); /* mute off */ | ||
1016 | if (!wavefront_fx_idle (dev)) return (-1); | ||
1017 | |||
1018 | return (0); | ||
1019 | } | ||
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c new file mode 100644 index 000000000000..6f51d64fb565 --- /dev/null +++ b/sound/isa/wavefront/wavefront_midi.c | |||
@@ -0,0 +1,570 @@ | |||
1 | /* | ||
2 | * Copyright (C) by Paul Barton-Davis 1998-1999 | ||
3 | * | ||
4 | * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) | ||
5 | * Version 2 (June 1991). See the "COPYING" file distributed with this | ||
6 | * software for more info. | ||
7 | */ | ||
8 | |||
9 | /* The low level driver for the WaveFront ICS2115 MIDI interface(s) | ||
10 | * | ||
11 | * Note that there is also an MPU-401 emulation (actually, a UART-401 | ||
12 | * emulation) on the CS4232 on the Tropez and Tropez Plus. This code | ||
13 | * has nothing to do with that interface at all. | ||
14 | * | ||
15 | * The interface is essentially just a UART-401, but is has the | ||
16 | * interesting property of supporting what Turtle Beach called | ||
17 | * "Virtual MIDI" mode. In this mode, there are effectively *two* | ||
18 | * MIDI buses accessible via the interface, one that is routed | ||
19 | * solely to/from the external WaveFront synthesizer and the other | ||
20 | * corresponding to the pin/socket connector used to link external | ||
21 | * MIDI devices to the board. | ||
22 | * | ||
23 | * This driver fully supports this mode, allowing two distinct MIDI | ||
24 | * busses to be used completely independently, giving 32 channels of | ||
25 | * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI | ||
26 | * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1, | ||
27 | * where `n' is the card number. Note that the device numbers may be | ||
28 | * something other than 0 and 1 if the CS4232 UART/MPU-401 interface | ||
29 | * is enabled. | ||
30 | * | ||
31 | * Switching between the two is accomplished externally by the driver | ||
32 | * using the two otherwise unused MIDI bytes. See the code for more details. | ||
33 | * | ||
34 | * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c) | ||
35 | * | ||
36 | * The main reason to turn off Virtual MIDI mode is when you want to | ||
37 | * tightly couple the WaveFront synth with an external MIDI | ||
38 | * device. You won't be able to distinguish the source of any MIDI | ||
39 | * data except via SysEx ID, but thats probably OK, since for the most | ||
40 | * part, the WaveFront won't be sending any MIDI data at all. | ||
41 | * | ||
42 | * The main reason to turn on Virtual MIDI Mode is to provide two | ||
43 | * completely independent 16-channel MIDI buses, one to the | ||
44 | * WaveFront and one to any external MIDI devices. Given the 32 | ||
45 | * voice nature of the WaveFront, its pretty easy to find a use | ||
46 | * for all 16 channels driving just that synth. | ||
47 | * | ||
48 | */ | ||
49 | |||
50 | #include <sound/driver.h> | ||
51 | #include <asm/io.h> | ||
52 | #include <linux/init.h> | ||
53 | #include <linux/time.h> | ||
54 | #include <linux/wait.h> | ||
55 | #include <sound/core.h> | ||
56 | #include <sound/snd_wavefront.h> | ||
57 | |||
58 | static inline int | ||
59 | wf_mpu_status (snd_wavefront_midi_t *midi) | ||
60 | |||
61 | { | ||
62 | return inb (midi->mpu_status_port); | ||
63 | } | ||
64 | |||
65 | static inline int | ||
66 | input_avail (snd_wavefront_midi_t *midi) | ||
67 | |||
68 | { | ||
69 | return !(wf_mpu_status(midi) & INPUT_AVAIL); | ||
70 | } | ||
71 | |||
72 | static inline int | ||
73 | output_ready (snd_wavefront_midi_t *midi) | ||
74 | |||
75 | { | ||
76 | return !(wf_mpu_status(midi) & OUTPUT_READY); | ||
77 | } | ||
78 | |||
79 | static inline int | ||
80 | read_data (snd_wavefront_midi_t *midi) | ||
81 | |||
82 | { | ||
83 | return inb (midi->mpu_data_port); | ||
84 | } | ||
85 | |||
86 | static inline void | ||
87 | write_data (snd_wavefront_midi_t *midi, unsigned char byte) | ||
88 | |||
89 | { | ||
90 | outb (byte, midi->mpu_data_port); | ||
91 | } | ||
92 | |||
93 | static snd_wavefront_midi_t * | ||
94 | get_wavefront_midi (snd_rawmidi_substream_t *substream) | ||
95 | |||
96 | { | ||
97 | snd_card_t *card; | ||
98 | snd_wavefront_card_t *acard; | ||
99 | |||
100 | if (substream == NULL || substream->rmidi == NULL) | ||
101 | return NULL; | ||
102 | |||
103 | card = substream->rmidi->card; | ||
104 | |||
105 | if (card == NULL) | ||
106 | return NULL; | ||
107 | |||
108 | if (card->private_data == NULL) | ||
109 | return NULL; | ||
110 | |||
111 | acard = card->private_data; | ||
112 | |||
113 | return &acard->wavefront.midi; | ||
114 | } | ||
115 | |||
116 | static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card) | ||
117 | { | ||
118 | snd_wavefront_midi_t *midi = &card->wavefront.midi; | ||
119 | snd_wavefront_mpu_id mpu; | ||
120 | unsigned long flags; | ||
121 | unsigned char midi_byte; | ||
122 | int max = 256, mask = 1; | ||
123 | int timeout; | ||
124 | |||
125 | /* Its not OK to try to change the status of "virtuality" of | ||
126 | the MIDI interface while we're outputting stuff. See | ||
127 | snd_wavefront_midi_{enable,disable}_virtual () for the | ||
128 | other half of this. | ||
129 | |||
130 | The first loop attempts to flush any data from the | ||
131 | current output device, and then the second | ||
132 | emits the switch byte (if necessary), and starts | ||
133 | outputting data for the output device currently in use. | ||
134 | */ | ||
135 | |||
136 | if (midi->substream_output[midi->output_mpu] == NULL) { | ||
137 | goto __second; | ||
138 | } | ||
139 | |||
140 | while (max > 0) { | ||
141 | |||
142 | /* XXX fix me - no hard timing loops allowed! */ | ||
143 | |||
144 | for (timeout = 30000; timeout > 0; timeout--) { | ||
145 | if (output_ready (midi)) | ||
146 | break; | ||
147 | } | ||
148 | |||
149 | spin_lock_irqsave (&midi->virtual, flags); | ||
150 | if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) { | ||
151 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
152 | goto __second; | ||
153 | } | ||
154 | if (output_ready (midi)) { | ||
155 | if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) { | ||
156 | if (!midi->isvirtual || | ||
157 | (midi_byte != WF_INTERNAL_SWITCH && | ||
158 | midi_byte != WF_EXTERNAL_SWITCH)) | ||
159 | write_data(midi, midi_byte); | ||
160 | max--; | ||
161 | } else { | ||
162 | if (midi->istimer) { | ||
163 | if (--midi->istimer <= 0) | ||
164 | del_timer(&midi->timer); | ||
165 | } | ||
166 | midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; | ||
167 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
168 | goto __second; | ||
169 | } | ||
170 | } else { | ||
171 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
172 | return; | ||
173 | } | ||
174 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
175 | } | ||
176 | |||
177 | __second: | ||
178 | |||
179 | if (midi->substream_output[!midi->output_mpu] == NULL) { | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | while (max > 0) { | ||
184 | |||
185 | /* XXX fix me - no hard timing loops allowed! */ | ||
186 | |||
187 | for (timeout = 30000; timeout > 0; timeout--) { | ||
188 | if (output_ready (midi)) | ||
189 | break; | ||
190 | } | ||
191 | |||
192 | spin_lock_irqsave (&midi->virtual, flags); | ||
193 | if (!midi->isvirtual) | ||
194 | mask = 0; | ||
195 | mpu = midi->output_mpu ^ mask; | ||
196 | mask = 0; /* don't invert the value from now */ | ||
197 | if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) { | ||
198 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
199 | return; | ||
200 | } | ||
201 | if (snd_rawmidi_transmit_empty(midi->substream_output[mpu])) | ||
202 | goto __timer; | ||
203 | if (output_ready (midi)) { | ||
204 | if (mpu != midi->output_mpu) { | ||
205 | write_data(midi, mpu == internal_mpu ? | ||
206 | WF_INTERNAL_SWITCH : | ||
207 | WF_EXTERNAL_SWITCH); | ||
208 | midi->output_mpu = mpu; | ||
209 | } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) { | ||
210 | if (!midi->isvirtual || | ||
211 | (midi_byte != WF_INTERNAL_SWITCH && | ||
212 | midi_byte != WF_EXTERNAL_SWITCH)) | ||
213 | write_data(midi, midi_byte); | ||
214 | max--; | ||
215 | } else { | ||
216 | __timer: | ||
217 | if (midi->istimer) { | ||
218 | if (--midi->istimer <= 0) | ||
219 | del_timer(&midi->timer); | ||
220 | } | ||
221 | midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; | ||
222 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
223 | return; | ||
224 | } | ||
225 | } else { | ||
226 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
227 | return; | ||
228 | } | ||
229 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream) | ||
234 | { | ||
235 | unsigned long flags; | ||
236 | snd_wavefront_midi_t *midi; | ||
237 | snd_wavefront_mpu_id mpu; | ||
238 | |||
239 | snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); | ||
240 | snd_assert(substream->rmidi->private_data != NULL, return -EIO); | ||
241 | |||
242 | mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); | ||
243 | |||
244 | if ((midi = get_wavefront_midi (substream)) == NULL) | ||
245 | return -EIO; | ||
246 | |||
247 | spin_lock_irqsave (&midi->open, flags); | ||
248 | midi->mode[mpu] |= MPU401_MODE_INPUT; | ||
249 | midi->substream_input[mpu] = substream; | ||
250 | spin_unlock_irqrestore (&midi->open, flags); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream) | ||
256 | { | ||
257 | unsigned long flags; | ||
258 | snd_wavefront_midi_t *midi; | ||
259 | snd_wavefront_mpu_id mpu; | ||
260 | |||
261 | snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); | ||
262 | snd_assert(substream->rmidi->private_data != NULL, return -EIO); | ||
263 | |||
264 | mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); | ||
265 | |||
266 | if ((midi = get_wavefront_midi (substream)) == NULL) | ||
267 | return -EIO; | ||
268 | |||
269 | spin_lock_irqsave (&midi->open, flags); | ||
270 | midi->mode[mpu] |= MPU401_MODE_OUTPUT; | ||
271 | midi->substream_output[mpu] = substream; | ||
272 | spin_unlock_irqrestore (&midi->open, flags); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream) | ||
278 | { | ||
279 | unsigned long flags; | ||
280 | snd_wavefront_midi_t *midi; | ||
281 | snd_wavefront_mpu_id mpu; | ||
282 | |||
283 | snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); | ||
284 | snd_assert(substream->rmidi->private_data != NULL, return -EIO); | ||
285 | |||
286 | mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); | ||
287 | |||
288 | if ((midi = get_wavefront_midi (substream)) == NULL) | ||
289 | return -EIO; | ||
290 | |||
291 | spin_lock_irqsave (&midi->open, flags); | ||
292 | midi->mode[mpu] &= ~MPU401_MODE_INPUT; | ||
293 | spin_unlock_irqrestore (&midi->open, flags); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream) | ||
299 | { | ||
300 | unsigned long flags; | ||
301 | snd_wavefront_midi_t *midi; | ||
302 | snd_wavefront_mpu_id mpu; | ||
303 | |||
304 | snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); | ||
305 | snd_assert(substream->rmidi->private_data != NULL, return -EIO); | ||
306 | |||
307 | mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); | ||
308 | |||
309 | if ((midi = get_wavefront_midi (substream)) == NULL) | ||
310 | return -EIO; | ||
311 | |||
312 | spin_lock_irqsave (&midi->open, flags); | ||
313 | midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; | ||
314 | spin_unlock_irqrestore (&midi->open, flags); | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
319 | { | ||
320 | unsigned long flags; | ||
321 | snd_wavefront_midi_t *midi; | ||
322 | snd_wavefront_mpu_id mpu; | ||
323 | |||
324 | if (substream == NULL || substream->rmidi == NULL) | ||
325 | return; | ||
326 | |||
327 | if (substream->rmidi->private_data == NULL) | ||
328 | return; | ||
329 | |||
330 | mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); | ||
331 | |||
332 | if ((midi = get_wavefront_midi (substream)) == NULL) { | ||
333 | return; | ||
334 | } | ||
335 | |||
336 | spin_lock_irqsave (&midi->virtual, flags); | ||
337 | if (up) { | ||
338 | midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER; | ||
339 | } else { | ||
340 | midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER; | ||
341 | } | ||
342 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
343 | } | ||
344 | |||
345 | static void snd_wavefront_midi_output_timer(unsigned long data) | ||
346 | { | ||
347 | snd_wavefront_card_t *card = (snd_wavefront_card_t *)data; | ||
348 | snd_wavefront_midi_t *midi = &card->wavefront.midi; | ||
349 | unsigned long flags; | ||
350 | |||
351 | spin_lock_irqsave (&midi->virtual, flags); | ||
352 | midi->timer.expires = 1 + jiffies; | ||
353 | add_timer(&midi->timer); | ||
354 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
355 | snd_wavefront_midi_output_write(card); | ||
356 | } | ||
357 | |||
358 | static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
359 | { | ||
360 | unsigned long flags; | ||
361 | snd_wavefront_midi_t *midi; | ||
362 | snd_wavefront_mpu_id mpu; | ||
363 | |||
364 | if (substream == NULL || substream->rmidi == NULL) | ||
365 | return; | ||
366 | |||
367 | if (substream->rmidi->private_data == NULL) | ||
368 | return; | ||
369 | |||
370 | mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); | ||
371 | |||
372 | if ((midi = get_wavefront_midi (substream)) == NULL) { | ||
373 | return; | ||
374 | } | ||
375 | |||
376 | spin_lock_irqsave (&midi->virtual, flags); | ||
377 | if (up) { | ||
378 | if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { | ||
379 | if (!midi->istimer) { | ||
380 | init_timer(&midi->timer); | ||
381 | midi->timer.function = snd_wavefront_midi_output_timer; | ||
382 | midi->timer.data = (unsigned long) substream->rmidi->card->private_data; | ||
383 | midi->timer.expires = 1 + jiffies; | ||
384 | add_timer(&midi->timer); | ||
385 | } | ||
386 | midi->istimer++; | ||
387 | midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER; | ||
388 | } | ||
389 | } else { | ||
390 | midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; | ||
391 | } | ||
392 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
393 | |||
394 | if (up) | ||
395 | snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data); | ||
396 | } | ||
397 | |||
398 | void | ||
399 | snd_wavefront_midi_interrupt (snd_wavefront_card_t *card) | ||
400 | |||
401 | { | ||
402 | unsigned long flags; | ||
403 | snd_wavefront_midi_t *midi; | ||
404 | static snd_rawmidi_substream_t *substream = NULL; | ||
405 | static int mpu = external_mpu; | ||
406 | int max = 128; | ||
407 | unsigned char byte; | ||
408 | |||
409 | midi = &card->wavefront.midi; | ||
410 | |||
411 | if (!input_avail (midi)) { /* not for us */ | ||
412 | snd_wavefront_midi_output_write(card); | ||
413 | return; | ||
414 | } | ||
415 | |||
416 | spin_lock_irqsave (&midi->virtual, flags); | ||
417 | while (--max) { | ||
418 | |||
419 | if (input_avail (midi)) { | ||
420 | byte = read_data (midi); | ||
421 | |||
422 | if (midi->isvirtual) { | ||
423 | if (byte == WF_EXTERNAL_SWITCH) { | ||
424 | substream = midi->substream_input[external_mpu]; | ||
425 | mpu = external_mpu; | ||
426 | } else if (byte == WF_INTERNAL_SWITCH) { | ||
427 | substream = midi->substream_output[internal_mpu]; | ||
428 | mpu = internal_mpu; | ||
429 | } /* else just leave it as it is */ | ||
430 | } else { | ||
431 | substream = midi->substream_input[internal_mpu]; | ||
432 | mpu = internal_mpu; | ||
433 | } | ||
434 | |||
435 | if (substream == NULL) { | ||
436 | continue; | ||
437 | } | ||
438 | |||
439 | if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) { | ||
440 | snd_rawmidi_receive(substream, &byte, 1); | ||
441 | } | ||
442 | } else { | ||
443 | break; | ||
444 | } | ||
445 | } | ||
446 | spin_unlock_irqrestore (&midi->virtual, flags); | ||
447 | |||
448 | snd_wavefront_midi_output_write(card); | ||
449 | } | ||
450 | |||
451 | void | ||
452 | snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card) | ||
453 | |||
454 | { | ||
455 | unsigned long flags; | ||
456 | |||
457 | spin_lock_irqsave (&card->wavefront.midi.virtual, flags); | ||
458 | card->wavefront.midi.isvirtual = 1; | ||
459 | card->wavefront.midi.output_mpu = internal_mpu; | ||
460 | card->wavefront.midi.input_mpu = internal_mpu; | ||
461 | spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); | ||
462 | } | ||
463 | |||
464 | void | ||
465 | snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) | ||
466 | |||
467 | { | ||
468 | unsigned long flags; | ||
469 | |||
470 | spin_lock_irqsave (&card->wavefront.midi.virtual, flags); | ||
471 | // snd_wavefront_midi_input_close (card->ics2115_external_rmidi); | ||
472 | // snd_wavefront_midi_output_close (card->ics2115_external_rmidi); | ||
473 | card->wavefront.midi.isvirtual = 0; | ||
474 | spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); | ||
475 | } | ||
476 | |||
477 | int __init | ||
478 | snd_wavefront_midi_start (snd_wavefront_card_t *card) | ||
479 | |||
480 | { | ||
481 | int ok, i; | ||
482 | unsigned char rbuf[4], wbuf[4]; | ||
483 | snd_wavefront_t *dev; | ||
484 | snd_wavefront_midi_t *midi; | ||
485 | |||
486 | dev = &card->wavefront; | ||
487 | midi = &dev->midi; | ||
488 | |||
489 | /* The ICS2115 MPU-401 interface doesn't do anything | ||
490 | until its set into UART mode. | ||
491 | */ | ||
492 | |||
493 | /* XXX fix me - no hard timing loops allowed! */ | ||
494 | |||
495 | for (i = 0; i < 30000 && !output_ready (midi); i++); | ||
496 | |||
497 | if (!output_ready (midi)) { | ||
498 | snd_printk ("MIDI interface not ready for command\n"); | ||
499 | return -1; | ||
500 | } | ||
501 | |||
502 | /* Any interrupts received from now on | ||
503 | are owned by the MIDI side of things. | ||
504 | */ | ||
505 | |||
506 | dev->interrupts_are_midi = 1; | ||
507 | |||
508 | outb (UART_MODE_ON, midi->mpu_command_port); | ||
509 | |||
510 | for (ok = 0, i = 50000; i > 0 && !ok; i--) { | ||
511 | if (input_avail (midi)) { | ||
512 | if (read_data (midi) == MPU_ACK) { | ||
513 | ok = 1; | ||
514 | break; | ||
515 | } | ||
516 | } | ||
517 | } | ||
518 | |||
519 | if (!ok) { | ||
520 | snd_printk ("cannot set UART mode for MIDI interface"); | ||
521 | dev->interrupts_are_midi = 0; | ||
522 | return -1; | ||
523 | } | ||
524 | |||
525 | /* Route external MIDI to WaveFront synth (by default) */ | ||
526 | |||
527 | if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) { | ||
528 | snd_printk ("can't enable MIDI-IN-2-synth routing.\n"); | ||
529 | /* XXX error ? */ | ||
530 | } | ||
531 | |||
532 | /* Turn on Virtual MIDI, but first *always* turn it off, | ||
533 | since otherwise consectutive reloads of the driver will | ||
534 | never cause the hardware to generate the initial "internal" or | ||
535 | "external" source bytes in the MIDI data stream. This | ||
536 | is pretty important, since the internal hardware generally will | ||
537 | be used to generate none or very little MIDI output, and | ||
538 | thus the only source of MIDI data is actually external. Without | ||
539 | the switch bytes, the driver will think it all comes from | ||
540 | the internal interface. Duh. | ||
541 | */ | ||
542 | |||
543 | if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { | ||
544 | snd_printk ("virtual MIDI mode not disabled\n"); | ||
545 | return 0; /* We're OK, but missing the external MIDI dev */ | ||
546 | } | ||
547 | |||
548 | snd_wavefront_midi_enable_virtual (card); | ||
549 | |||
550 | if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) { | ||
551 | snd_printk ("cannot enable virtual MIDI mode.\n"); | ||
552 | snd_wavefront_midi_disable_virtual (card); | ||
553 | } | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | snd_rawmidi_ops_t snd_wavefront_midi_output = | ||
558 | { | ||
559 | .open = snd_wavefront_midi_output_open, | ||
560 | .close = snd_wavefront_midi_output_close, | ||
561 | .trigger = snd_wavefront_midi_output_trigger, | ||
562 | }; | ||
563 | |||
564 | snd_rawmidi_ops_t snd_wavefront_midi_input = | ||
565 | { | ||
566 | .open = snd_wavefront_midi_input_open, | ||
567 | .close = snd_wavefront_midi_input_close, | ||
568 | .trigger = snd_wavefront_midi_input_trigger, | ||
569 | }; | ||
570 | |||
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c new file mode 100644 index 000000000000..0c3c951009d8 --- /dev/null +++ b/sound/isa/wavefront/wavefront_synth.c | |||
@@ -0,0 +1,2243 @@ | |||
1 | /* Copyright (C) by Paul Barton-Davis 1998-1999 | ||
2 | * | ||
3 | * Some portions of this file are taken from work that is | ||
4 | * copyright (C) by Hannu Savolainen 1993-1996 | ||
5 | * | ||
6 | * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) | ||
7 | * Version 2 (June 1991). See the "COPYING" file distributed with this software | ||
8 | * for more info. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth | ||
13 | * (Maui, Tropez, Tropez Plus) | ||
14 | * | ||
15 | * This driver supports the onboard wavetable synthesizer (an ICS2115), | ||
16 | * including patch, sample and program loading and unloading, conversion | ||
17 | * of GUS patches during loading, and full user-level access to all | ||
18 | * WaveFront commands. It tries to provide semi-intelligent patch and | ||
19 | * sample management as well. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <linux/wait.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/snd_wavefront.h> | ||
33 | #include <sound/initval.h> | ||
34 | |||
35 | static int wf_raw = 0; /* we normally check for "raw state" to firmware | ||
36 | loading. if non-zero, then during driver loading, the | ||
37 | state of the board is ignored, and we reset the | ||
38 | board and load the firmware anyway. | ||
39 | */ | ||
40 | |||
41 | static int fx_raw = 1; /* if this is zero, we'll leave the FX processor in | ||
42 | whatever state it is when the driver is loaded. | ||
43 | The default is to download the microprogram and | ||
44 | associated coefficients to set it up for "default" | ||
45 | operation, whatever that means. | ||
46 | */ | ||
47 | |||
48 | static int debug_default = 0; /* you can set this to control debugging | ||
49 | during driver loading. it takes any combination | ||
50 | of the WF_DEBUG_* flags defined in | ||
51 | wavefront.h | ||
52 | */ | ||
53 | |||
54 | /* XXX this needs to be made firmware and hardware version dependent */ | ||
55 | |||
56 | static char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed | ||
57 | version of the WaveFront OS | ||
58 | */ | ||
59 | |||
60 | static int wait_usecs = 150; /* This magic number seems to give pretty optimal | ||
61 | throughput based on my limited experimentation. | ||
62 | If you want to play around with it and find a better | ||
63 | value, be my guest. Remember, the idea is to | ||
64 | get a number that causes us to just busy wait | ||
65 | for as many WaveFront commands as possible, without | ||
66 | coming up with a number so large that we hog the | ||
67 | whole CPU. | ||
68 | |||
69 | Specifically, with this number, out of about 134,000 | ||
70 | status waits, only about 250 result in a sleep. | ||
71 | */ | ||
72 | |||
73 | static int sleep_interval = 100; /* HZ/sleep_interval seconds per sleep */ | ||
74 | static int sleep_tries = 50; /* number of times we'll try to sleep */ | ||
75 | |||
76 | static int reset_time = 2; /* hundreths of a second we wait after a HW | ||
77 | reset for the expected interrupt. | ||
78 | */ | ||
79 | |||
80 | static int ramcheck_time = 20; /* time in seconds to wait while ROM code | ||
81 | checks on-board RAM. | ||
82 | */ | ||
83 | |||
84 | static int osrun_time = 10; /* time in seconds we wait for the OS to | ||
85 | start running. | ||
86 | */ | ||
87 | module_param(wf_raw, int, 0444); | ||
88 | MODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS"); | ||
89 | module_param(fx_raw, int, 0444); | ||
90 | MODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help"); | ||
91 | module_param(debug_default, int, 0444); | ||
92 | MODULE_PARM_DESC(debug_default, "debug parameters for card initialization"); | ||
93 | module_param(wait_usecs, int, 0444); | ||
94 | MODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs"); | ||
95 | module_param(sleep_interval, int, 0444); | ||
96 | MODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply"); | ||
97 | module_param(sleep_tries, int, 0444); | ||
98 | MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait"); | ||
99 | module_param(ospath, charp, 0444); | ||
100 | MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware"); | ||
101 | module_param(reset_time, int, 0444); | ||
102 | MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect"); | ||
103 | module_param(ramcheck_time, int, 0444); | ||
104 | MODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test"); | ||
105 | module_param(osrun_time, int, 0444); | ||
106 | MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS"); | ||
107 | |||
108 | /* if WF_DEBUG not defined, no run-time debugging messages will | ||
109 | be available via the debug flag setting. Given the current | ||
110 | beta state of the driver, this will remain set until a future | ||
111 | version. | ||
112 | */ | ||
113 | |||
114 | #define WF_DEBUG 1 | ||
115 | |||
116 | #ifdef WF_DEBUG | ||
117 | |||
118 | #if defined(NEW_MACRO_VARARGS) || __GNUC__ >= 3 | ||
119 | #define DPRINT(cond, ...) \ | ||
120 | if ((dev->debug & (cond)) == (cond)) { \ | ||
121 | snd_printk (__VA_ARGS__); \ | ||
122 | } | ||
123 | #else | ||
124 | #define DPRINT(cond, args...) \ | ||
125 | if ((dev->debug & (cond)) == (cond)) { \ | ||
126 | snd_printk (args); \ | ||
127 | } | ||
128 | #endif | ||
129 | #else | ||
130 | #define DPRINT(cond, args...) | ||
131 | #endif /* WF_DEBUG */ | ||
132 | |||
133 | #define LOGNAME "WaveFront: " | ||
134 | |||
135 | /* bitmasks for WaveFront status port value */ | ||
136 | |||
137 | #define STAT_RINTR_ENABLED 0x01 | ||
138 | #define STAT_CAN_READ 0x02 | ||
139 | #define STAT_INTR_READ 0x04 | ||
140 | #define STAT_WINTR_ENABLED 0x10 | ||
141 | #define STAT_CAN_WRITE 0x20 | ||
142 | #define STAT_INTR_WRITE 0x40 | ||
143 | |||
144 | static int wavefront_delete_sample (snd_wavefront_t *, int sampnum); | ||
145 | static int wavefront_find_free_sample (snd_wavefront_t *); | ||
146 | |||
147 | typedef struct { | ||
148 | int cmd; | ||
149 | char *action; | ||
150 | unsigned int read_cnt; | ||
151 | unsigned int write_cnt; | ||
152 | int need_ack; | ||
153 | } wavefront_command; | ||
154 | |||
155 | static struct { | ||
156 | int errno; | ||
157 | const char *errstr; | ||
158 | } wavefront_errors[] = { | ||
159 | { 0x01, "Bad sample number" }, | ||
160 | { 0x02, "Out of sample memory" }, | ||
161 | { 0x03, "Bad patch number" }, | ||
162 | { 0x04, "Error in number of voices" }, | ||
163 | { 0x06, "Sample load already in progress" }, | ||
164 | { 0x0B, "No sample load request pending" }, | ||
165 | { 0x0E, "Bad MIDI channel number" }, | ||
166 | { 0x10, "Download Record Error" }, | ||
167 | { 0x80, "Success" }, | ||
168 | { 0x0 } | ||
169 | }; | ||
170 | |||
171 | #define NEEDS_ACK 1 | ||
172 | |||
173 | static wavefront_command wavefront_commands[] = { | ||
174 | { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, | ||
175 | { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, | ||
176 | { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, | ||
177 | { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, | ||
178 | { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, | ||
179 | { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, | ||
180 | { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, | ||
181 | { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, | ||
182 | { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, | ||
183 | { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, | ||
184 | { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, | ||
185 | { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, | ||
186 | { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, | ||
187 | { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, | ||
188 | { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, | ||
189 | { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, | ||
190 | { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, | ||
191 | { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, | ||
192 | { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, | ||
193 | { WFC_DOWNLOAD_SAMPLE, "download sample", | ||
194 | 0, WF_SAMPLE_BYTES, NEEDS_ACK }, | ||
195 | { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, | ||
196 | { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", | ||
197 | 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, | ||
198 | { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, | ||
199 | |||
200 | /* This command requires a variable number of bytes to be written. | ||
201 | There is a hack in snd_wavefront_cmd() to support this. The actual | ||
202 | count is passed in as the read buffer ptr, cast appropriately. | ||
203 | Ugh. | ||
204 | */ | ||
205 | |||
206 | { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, | ||
207 | |||
208 | /* This one is a hack as well. We just read the first byte of the | ||
209 | response, don't fetch an ACK, and leave the rest to the | ||
210 | calling function. Ugly, ugly, ugly. | ||
211 | */ | ||
212 | |||
213 | { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, | ||
214 | { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", | ||
215 | 0, WF_ALIAS_BYTES, NEEDS_ACK }, | ||
216 | { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, | ||
217 | { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, | ||
218 | { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, | ||
219 | { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, | ||
220 | { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, | ||
221 | { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, | ||
222 | { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, | ||
223 | { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, | ||
224 | { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, | ||
225 | { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, | ||
226 | NEEDS_ACK}, | ||
227 | { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, | ||
228 | { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", | ||
229 | 0, 1, NEEDS_ACK }, | ||
230 | { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, | ||
231 | { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", | ||
232 | 32, 0, 0 }, | ||
233 | { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, | ||
234 | { 0x00 } | ||
235 | }; | ||
236 | |||
237 | static const char * | ||
238 | wavefront_errorstr (int errnum) | ||
239 | |||
240 | { | ||
241 | int i; | ||
242 | |||
243 | for (i = 0; wavefront_errors[i].errstr; i++) { | ||
244 | if (wavefront_errors[i].errno == errnum) { | ||
245 | return wavefront_errors[i].errstr; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | return "Unknown WaveFront error"; | ||
250 | } | ||
251 | |||
252 | static wavefront_command * | ||
253 | wavefront_get_command (int cmd) | ||
254 | |||
255 | { | ||
256 | int i; | ||
257 | |||
258 | for (i = 0; wavefront_commands[i].cmd != 0; i++) { | ||
259 | if (cmd == wavefront_commands[i].cmd) { | ||
260 | return &wavefront_commands[i]; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | return (wavefront_command *) 0; | ||
265 | } | ||
266 | |||
267 | static inline int | ||
268 | wavefront_status (snd_wavefront_t *dev) | ||
269 | |||
270 | { | ||
271 | return inb (dev->status_port); | ||
272 | } | ||
273 | |||
274 | static int | ||
275 | wavefront_sleep (int limit) | ||
276 | |||
277 | { | ||
278 | set_current_state(TASK_INTERRUPTIBLE); | ||
279 | schedule_timeout(limit); | ||
280 | |||
281 | return signal_pending(current); | ||
282 | } | ||
283 | |||
284 | static int | ||
285 | wavefront_wait (snd_wavefront_t *dev, int mask) | ||
286 | |||
287 | { | ||
288 | int i; | ||
289 | |||
290 | /* Spin for a short period of time, because >99% of all | ||
291 | requests to the WaveFront can be serviced inline like this. | ||
292 | */ | ||
293 | |||
294 | for (i = 0; i < wait_usecs; i += 5) { | ||
295 | if (wavefront_status (dev) & mask) { | ||
296 | return 1; | ||
297 | } | ||
298 | udelay(5); | ||
299 | } | ||
300 | |||
301 | for (i = 0; i < sleep_tries; i++) { | ||
302 | |||
303 | if (wavefront_status (dev) & mask) { | ||
304 | return 1; | ||
305 | } | ||
306 | |||
307 | if (wavefront_sleep (HZ/sleep_interval)) { | ||
308 | return (0); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | return (0); | ||
313 | } | ||
314 | |||
315 | static int | ||
316 | wavefront_read (snd_wavefront_t *dev) | ||
317 | |||
318 | { | ||
319 | if (wavefront_wait (dev, STAT_CAN_READ)) | ||
320 | return inb (dev->data_port); | ||
321 | |||
322 | DPRINT (WF_DEBUG_DATA, "read timeout.\n"); | ||
323 | |||
324 | return -1; | ||
325 | } | ||
326 | |||
327 | static int | ||
328 | wavefront_write (snd_wavefront_t *dev, unsigned char data) | ||
329 | |||
330 | { | ||
331 | if (wavefront_wait (dev, STAT_CAN_WRITE)) { | ||
332 | outb (data, dev->data_port); | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | DPRINT (WF_DEBUG_DATA, "write timeout.\n"); | ||
337 | |||
338 | return -1; | ||
339 | } | ||
340 | |||
341 | int | ||
342 | snd_wavefront_cmd (snd_wavefront_t *dev, | ||
343 | int cmd, unsigned char *rbuf, unsigned char *wbuf) | ||
344 | |||
345 | { | ||
346 | int ack; | ||
347 | unsigned int i; | ||
348 | int c; | ||
349 | wavefront_command *wfcmd; | ||
350 | |||
351 | if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { | ||
352 | snd_printk ("command 0x%x not supported.\n", | ||
353 | cmd); | ||
354 | return 1; | ||
355 | } | ||
356 | |||
357 | /* Hack to handle the one variable-size write command. See | ||
358 | wavefront_send_multisample() for the other half of this | ||
359 | gross and ugly strategy. | ||
360 | */ | ||
361 | |||
362 | if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { | ||
363 | wfcmd->write_cnt = (unsigned long) rbuf; | ||
364 | rbuf = NULL; | ||
365 | } | ||
366 | |||
367 | DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", | ||
368 | cmd, wfcmd->action, wfcmd->read_cnt, | ||
369 | wfcmd->write_cnt, wfcmd->need_ack); | ||
370 | |||
371 | if (wavefront_write (dev, cmd)) { | ||
372 | DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " | ||
373 | "0x%x [%s].\n", | ||
374 | cmd, wfcmd->action); | ||
375 | return 1; | ||
376 | } | ||
377 | |||
378 | if (wfcmd->write_cnt > 0) { | ||
379 | DPRINT (WF_DEBUG_DATA, "writing %d bytes " | ||
380 | "for 0x%x\n", | ||
381 | wfcmd->write_cnt, cmd); | ||
382 | |||
383 | for (i = 0; i < wfcmd->write_cnt; i++) { | ||
384 | if (wavefront_write (dev, wbuf[i])) { | ||
385 | DPRINT (WF_DEBUG_IO, "bad write for byte " | ||
386 | "%d of 0x%x [%s].\n", | ||
387 | i, cmd, wfcmd->action); | ||
388 | return 1; | ||
389 | } | ||
390 | |||
391 | DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", | ||
392 | i, wbuf[i]); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | if (wfcmd->read_cnt > 0) { | ||
397 | DPRINT (WF_DEBUG_DATA, "reading %d ints " | ||
398 | "for 0x%x\n", | ||
399 | wfcmd->read_cnt, cmd); | ||
400 | |||
401 | for (i = 0; i < wfcmd->read_cnt; i++) { | ||
402 | |||
403 | if ((c = wavefront_read (dev)) == -1) { | ||
404 | DPRINT (WF_DEBUG_IO, "bad read for byte " | ||
405 | "%d of 0x%x [%s].\n", | ||
406 | i, cmd, wfcmd->action); | ||
407 | return 1; | ||
408 | } | ||
409 | |||
410 | /* Now handle errors. Lots of special cases here */ | ||
411 | |||
412 | if (c == 0xff) { | ||
413 | if ((c = wavefront_read (dev)) == -1) { | ||
414 | DPRINT (WF_DEBUG_IO, "bad read for " | ||
415 | "error byte at " | ||
416 | "read byte %d " | ||
417 | "of 0x%x [%s].\n", | ||
418 | i, cmd, | ||
419 | wfcmd->action); | ||
420 | return 1; | ||
421 | } | ||
422 | |||
423 | /* Can you believe this madness ? */ | ||
424 | |||
425 | if (c == 1 && | ||
426 | wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { | ||
427 | rbuf[0] = WF_ST_EMPTY; | ||
428 | return (0); | ||
429 | |||
430 | } else if (c == 3 && | ||
431 | wfcmd->cmd == WFC_UPLOAD_PATCH) { | ||
432 | |||
433 | return 3; | ||
434 | |||
435 | } else if (c == 1 && | ||
436 | wfcmd->cmd == WFC_UPLOAD_PROGRAM) { | ||
437 | |||
438 | return 1; | ||
439 | |||
440 | } else { | ||
441 | |||
442 | DPRINT (WF_DEBUG_IO, "error %d (%s) " | ||
443 | "during " | ||
444 | "read for byte " | ||
445 | "%d of 0x%x " | ||
446 | "[%s].\n", | ||
447 | c, | ||
448 | wavefront_errorstr (c), | ||
449 | i, cmd, | ||
450 | wfcmd->action); | ||
451 | return 1; | ||
452 | |||
453 | } | ||
454 | |||
455 | } else { | ||
456 | rbuf[i] = c; | ||
457 | } | ||
458 | |||
459 | DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); | ||
460 | } | ||
461 | } | ||
462 | |||
463 | if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { | ||
464 | |||
465 | DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); | ||
466 | |||
467 | /* Some commands need an ACK, but return zero instead | ||
468 | of the standard value. | ||
469 | */ | ||
470 | |||
471 | if ((ack = wavefront_read (dev)) == 0) { | ||
472 | ack = WF_ACK; | ||
473 | } | ||
474 | |||
475 | if (ack != WF_ACK) { | ||
476 | if (ack == -1) { | ||
477 | DPRINT (WF_DEBUG_IO, "cannot read ack for " | ||
478 | "0x%x [%s].\n", | ||
479 | cmd, wfcmd->action); | ||
480 | return 1; | ||
481 | |||
482 | } else { | ||
483 | int err = -1; /* something unknown */ | ||
484 | |||
485 | if (ack == 0xff) { /* explicit error */ | ||
486 | |||
487 | if ((err = wavefront_read (dev)) == -1) { | ||
488 | DPRINT (WF_DEBUG_DATA, | ||
489 | "cannot read err " | ||
490 | "for 0x%x [%s].\n", | ||
491 | cmd, wfcmd->action); | ||
492 | } | ||
493 | } | ||
494 | |||
495 | DPRINT (WF_DEBUG_IO, "0x%x [%s] " | ||
496 | "failed (0x%x, 0x%x, %s)\n", | ||
497 | cmd, wfcmd->action, ack, err, | ||
498 | wavefront_errorstr (err)); | ||
499 | |||
500 | return -err; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | DPRINT (WF_DEBUG_DATA, "ack received " | ||
505 | "for 0x%x [%s]\n", | ||
506 | cmd, wfcmd->action); | ||
507 | } else { | ||
508 | |||
509 | DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " | ||
510 | "ACK (%d,%d,%d)\n", | ||
511 | cmd, wfcmd->action, wfcmd->read_cnt, | ||
512 | wfcmd->write_cnt, wfcmd->need_ack); | ||
513 | } | ||
514 | |||
515 | return 0; | ||
516 | |||
517 | } | ||
518 | |||
519 | /*********************************************************************** | ||
520 | WaveFront data munging | ||
521 | |||
522 | Things here are weird. All data written to the board cannot | ||
523 | have its most significant bit set. Any data item with values | ||
524 | potentially > 0x7F (127) must be split across multiple bytes. | ||
525 | |||
526 | Sometimes, we need to munge numeric values that are represented on | ||
527 | the x86 side as 8-32 bit values. Sometimes, we need to munge data | ||
528 | that is represented on the x86 side as an array of bytes. The most | ||
529 | efficient approach to handling both cases seems to be to use 2 | ||
530 | different functions for munging and 2 for de-munging. This avoids | ||
531 | weird casting and worrying about bit-level offsets. | ||
532 | |||
533 | **********************************************************************/ | ||
534 | |||
535 | static unsigned char * | ||
536 | munge_int32 (unsigned int src, | ||
537 | unsigned char *dst, | ||
538 | unsigned int dst_size) | ||
539 | { | ||
540 | unsigned int i; | ||
541 | |||
542 | for (i = 0; i < dst_size; i++) { | ||
543 | *dst = src & 0x7F; /* Mask high bit of LSB */ | ||
544 | src = src >> 7; /* Rotate Right 7 bits */ | ||
545 | /* Note: we leave the upper bits in place */ | ||
546 | |||
547 | dst++; | ||
548 | }; | ||
549 | return dst; | ||
550 | }; | ||
551 | |||
552 | static int | ||
553 | demunge_int32 (unsigned char* src, int src_size) | ||
554 | |||
555 | { | ||
556 | int i; | ||
557 | int outval = 0; | ||
558 | |||
559 | for (i = src_size - 1; i >= 0; i--) { | ||
560 | outval=(outval<<7)+src[i]; | ||
561 | } | ||
562 | |||
563 | return outval; | ||
564 | }; | ||
565 | |||
566 | static | ||
567 | unsigned char * | ||
568 | munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) | ||
569 | |||
570 | { | ||
571 | unsigned int i; | ||
572 | unsigned int last = dst_size / 2; | ||
573 | |||
574 | for (i = 0; i < last; i++) { | ||
575 | *dst++ = src[i] & 0x7f; | ||
576 | *dst++ = src[i] >> 7; | ||
577 | } | ||
578 | return dst; | ||
579 | } | ||
580 | |||
581 | static | ||
582 | unsigned char * | ||
583 | demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) | ||
584 | |||
585 | { | ||
586 | int i; | ||
587 | unsigned char *end = src + src_bytes; | ||
588 | |||
589 | end = src + src_bytes; | ||
590 | |||
591 | /* NOTE: src and dst *CAN* point to the same address */ | ||
592 | |||
593 | for (i = 0; src != end; i++) { | ||
594 | dst[i] = *src++; | ||
595 | dst[i] |= (*src++)<<7; | ||
596 | } | ||
597 | |||
598 | return dst; | ||
599 | } | ||
600 | |||
601 | /*********************************************************************** | ||
602 | WaveFront: sample, patch and program management. | ||
603 | ***********************************************************************/ | ||
604 | |||
605 | static int | ||
606 | wavefront_delete_sample (snd_wavefront_t *dev, int sample_num) | ||
607 | |||
608 | { | ||
609 | unsigned char wbuf[2]; | ||
610 | int x; | ||
611 | |||
612 | wbuf[0] = sample_num & 0x7f; | ||
613 | wbuf[1] = sample_num >> 7; | ||
614 | |||
615 | if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, NULL, wbuf)) == 0) { | ||
616 | dev->sample_status[sample_num] = WF_ST_EMPTY; | ||
617 | } | ||
618 | |||
619 | return x; | ||
620 | } | ||
621 | |||
622 | static int | ||
623 | wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom) | ||
624 | |||
625 | { | ||
626 | int i; | ||
627 | unsigned char rbuf[32], wbuf[32]; | ||
628 | unsigned int sc_real, sc_alias, sc_multi; | ||
629 | |||
630 | /* check sample status */ | ||
631 | |||
632 | if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) { | ||
633 | snd_printk ("cannot request sample count.\n"); | ||
634 | return -1; | ||
635 | } | ||
636 | |||
637 | sc_real = sc_alias = sc_multi = dev->samples_used = 0; | ||
638 | |||
639 | for (i = 0; i < WF_MAX_SAMPLE; i++) { | ||
640 | |||
641 | wbuf[0] = i & 0x7f; | ||
642 | wbuf[1] = i >> 7; | ||
643 | |||
644 | if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { | ||
645 | snd_printk("cannot identify sample " | ||
646 | "type of slot %d\n", i); | ||
647 | dev->sample_status[i] = WF_ST_EMPTY; | ||
648 | continue; | ||
649 | } | ||
650 | |||
651 | dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); | ||
652 | |||
653 | if (assume_rom) { | ||
654 | dev->sample_status[i] |= WF_SLOT_ROM; | ||
655 | } | ||
656 | |||
657 | switch (rbuf[0] & WF_ST_MASK) { | ||
658 | case WF_ST_SAMPLE: | ||
659 | sc_real++; | ||
660 | break; | ||
661 | case WF_ST_MULTISAMPLE: | ||
662 | sc_multi++; | ||
663 | break; | ||
664 | case WF_ST_ALIAS: | ||
665 | sc_alias++; | ||
666 | break; | ||
667 | case WF_ST_EMPTY: | ||
668 | break; | ||
669 | |||
670 | default: | ||
671 | snd_printk ("unknown sample type for " | ||
672 | "slot %d (0x%x)\n", | ||
673 | i, rbuf[0]); | ||
674 | } | ||
675 | |||
676 | if (rbuf[0] != WF_ST_EMPTY) { | ||
677 | dev->samples_used++; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | snd_printk ("%d samples used (%d real, %d aliases, %d multi), " | ||
682 | "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi, | ||
683 | WF_MAX_SAMPLE - dev->samples_used); | ||
684 | |||
685 | |||
686 | return (0); | ||
687 | |||
688 | } | ||
689 | |||
690 | static int | ||
691 | wavefront_get_patch_status (snd_wavefront_t *dev) | ||
692 | |||
693 | { | ||
694 | unsigned char patchbuf[WF_PATCH_BYTES]; | ||
695 | unsigned char patchnum[2]; | ||
696 | wavefront_patch *p; | ||
697 | int i, x, cnt, cnt2; | ||
698 | |||
699 | for (i = 0; i < WF_MAX_PATCH; i++) { | ||
700 | patchnum[0] = i & 0x7f; | ||
701 | patchnum[1] = i >> 7; | ||
702 | |||
703 | if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf, | ||
704 | patchnum)) == 0) { | ||
705 | |||
706 | dev->patch_status[i] |= WF_SLOT_FILLED; | ||
707 | p = (wavefront_patch *) patchbuf; | ||
708 | dev->sample_status | ||
709 | [p->sample_number|(p->sample_msb<<7)] |= | ||
710 | WF_SLOT_USED; | ||
711 | |||
712 | } else if (x == 3) { /* Bad patch number */ | ||
713 | dev->patch_status[i] = 0; | ||
714 | } else { | ||
715 | snd_printk ("upload patch " | ||
716 | "error 0x%x\n", x); | ||
717 | dev->patch_status[i] = 0; | ||
718 | return 1; | ||
719 | } | ||
720 | } | ||
721 | |||
722 | /* program status has already filled in slot_used bits */ | ||
723 | |||
724 | for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { | ||
725 | if (dev->patch_status[i] & WF_SLOT_FILLED) { | ||
726 | cnt++; | ||
727 | } | ||
728 | if (dev->patch_status[i] & WF_SLOT_USED) { | ||
729 | cnt2++; | ||
730 | } | ||
731 | |||
732 | } | ||
733 | snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2); | ||
734 | |||
735 | return (0); | ||
736 | } | ||
737 | |||
738 | static int | ||
739 | wavefront_get_program_status (snd_wavefront_t *dev) | ||
740 | |||
741 | { | ||
742 | unsigned char progbuf[WF_PROGRAM_BYTES]; | ||
743 | wavefront_program prog; | ||
744 | unsigned char prognum; | ||
745 | int i, x, l, cnt; | ||
746 | |||
747 | for (i = 0; i < WF_MAX_PROGRAM; i++) { | ||
748 | prognum = i; | ||
749 | |||
750 | if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf, | ||
751 | &prognum)) == 0) { | ||
752 | |||
753 | dev->prog_status[i] |= WF_SLOT_USED; | ||
754 | |||
755 | demunge_buf (progbuf, (unsigned char *) &prog, | ||
756 | WF_PROGRAM_BYTES); | ||
757 | |||
758 | for (l = 0; l < WF_NUM_LAYERS; l++) { | ||
759 | if (prog.layer[l].mute) { | ||
760 | dev->patch_status | ||
761 | [prog.layer[l].patch_number] |= | ||
762 | WF_SLOT_USED; | ||
763 | } | ||
764 | } | ||
765 | } else if (x == 1) { /* Bad program number */ | ||
766 | dev->prog_status[i] = 0; | ||
767 | } else { | ||
768 | snd_printk ("upload program " | ||
769 | "error 0x%x\n", x); | ||
770 | dev->prog_status[i] = 0; | ||
771 | } | ||
772 | } | ||
773 | |||
774 | for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { | ||
775 | if (dev->prog_status[i]) { | ||
776 | cnt++; | ||
777 | } | ||
778 | } | ||
779 | |||
780 | snd_printk ("%d programs slots in use\n", cnt); | ||
781 | |||
782 | return (0); | ||
783 | } | ||
784 | |||
785 | static int | ||
786 | wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) | ||
787 | |||
788 | { | ||
789 | unsigned char buf[WF_PATCH_BYTES+2]; | ||
790 | unsigned char *bptr; | ||
791 | |||
792 | DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", | ||
793 | header->number); | ||
794 | |||
795 | dev->patch_status[header->number] |= WF_SLOT_FILLED; | ||
796 | |||
797 | bptr = buf; | ||
798 | bptr = munge_int32 (header->number, buf, 2); | ||
799 | munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); | ||
800 | |||
801 | if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) { | ||
802 | snd_printk ("download patch failed\n"); | ||
803 | return -(EIO); | ||
804 | } | ||
805 | |||
806 | return (0); | ||
807 | } | ||
808 | |||
809 | static int | ||
810 | wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) | ||
811 | |||
812 | { | ||
813 | unsigned char buf[WF_PROGRAM_BYTES+1]; | ||
814 | int i; | ||
815 | |||
816 | DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", | ||
817 | header->number); | ||
818 | |||
819 | dev->prog_status[header->number] = WF_SLOT_USED; | ||
820 | |||
821 | /* XXX need to zero existing SLOT_USED bit for program_status[i] | ||
822 | where `i' is the program that's being (potentially) overwritten. | ||
823 | */ | ||
824 | |||
825 | for (i = 0; i < WF_NUM_LAYERS; i++) { | ||
826 | if (header->hdr.pr.layer[i].mute) { | ||
827 | dev->patch_status[header->hdr.pr.layer[i].patch_number] |= | ||
828 | WF_SLOT_USED; | ||
829 | |||
830 | /* XXX need to mark SLOT_USED for sample used by | ||
831 | patch_number, but this means we have to load it. Ick. | ||
832 | */ | ||
833 | } | ||
834 | } | ||
835 | |||
836 | buf[0] = header->number; | ||
837 | munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); | ||
838 | |||
839 | if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) { | ||
840 | snd_printk ("download patch failed\n"); | ||
841 | return -(EIO); | ||
842 | } | ||
843 | |||
844 | return (0); | ||
845 | } | ||
846 | |||
847 | static int | ||
848 | wavefront_freemem (snd_wavefront_t *dev) | ||
849 | |||
850 | { | ||
851 | char rbuf[8]; | ||
852 | |||
853 | if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, NULL)) { | ||
854 | snd_printk ("can't get memory stats.\n"); | ||
855 | return -1; | ||
856 | } else { | ||
857 | return demunge_int32 (rbuf, 4); | ||
858 | } | ||
859 | } | ||
860 | |||
861 | static int | ||
862 | wavefront_send_sample (snd_wavefront_t *dev, | ||
863 | wavefront_patch_info *header, | ||
864 | u16 __user *dataptr, | ||
865 | int data_is_unsigned) | ||
866 | |||
867 | { | ||
868 | /* samples are downloaded via a 16-bit wide i/o port | ||
869 | (you could think of it as 2 adjacent 8-bit wide ports | ||
870 | but its less efficient that way). therefore, all | ||
871 | the blocksizes and so forth listed in the documentation, | ||
872 | and used conventionally to refer to sample sizes, | ||
873 | which are given in 8-bit units (bytes), need to be | ||
874 | divided by 2. | ||
875 | */ | ||
876 | |||
877 | u16 sample_short; | ||
878 | u32 length; | ||
879 | u16 __user *data_end = NULL; | ||
880 | unsigned int i; | ||
881 | const unsigned int max_blksize = 4096/2; | ||
882 | unsigned int written; | ||
883 | unsigned int blocksize; | ||
884 | int dma_ack; | ||
885 | int blocknum; | ||
886 | unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; | ||
887 | unsigned char *shptr; | ||
888 | int skip = 0; | ||
889 | int initial_skip = 0; | ||
890 | |||
891 | DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " | ||
892 | "type %d, %d bytes from 0x%lx\n", | ||
893 | header->size ? "" : "header ", | ||
894 | header->number, header->subkey, | ||
895 | header->size, | ||
896 | (unsigned long) header->dataptr); | ||
897 | |||
898 | if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { | ||
899 | int x; | ||
900 | |||
901 | if ((x = wavefront_find_free_sample (dev)) < 0) { | ||
902 | return -ENOMEM; | ||
903 | } | ||
904 | snd_printk ("unspecified sample => %d\n", x); | ||
905 | header->number = x; | ||
906 | } | ||
907 | |||
908 | if (header->size) { | ||
909 | |||
910 | /* XXX it's a debatable point whether or not RDONLY semantics | ||
911 | on the ROM samples should cover just the sample data or | ||
912 | the sample header. For now, it only covers the sample data, | ||
913 | so anyone is free at all times to rewrite sample headers. | ||
914 | |||
915 | My reason for this is that we have the sample headers | ||
916 | available in the WFB file for General MIDI, and so these | ||
917 | can always be reset if needed. The sample data, however, | ||
918 | cannot be recovered without a complete reset and firmware | ||
919 | reload of the ICS2115, which is a very expensive operation. | ||
920 | |||
921 | So, doing things this way allows us to honor the notion of | ||
922 | "RESETSAMPLES" reasonably cheaply. Note however, that this | ||
923 | is done purely at user level: there is no WFB parser in | ||
924 | this driver, and so a complete reset (back to General MIDI, | ||
925 | or theoretically some other configuration) is the | ||
926 | responsibility of the user level library. | ||
927 | |||
928 | To try to do this in the kernel would be a little | ||
929 | crazy: we'd need 158K of kernel space just to hold | ||
930 | a copy of the patch/program/sample header data. | ||
931 | */ | ||
932 | |||
933 | if (dev->rom_samples_rdonly) { | ||
934 | if (dev->sample_status[header->number] & WF_SLOT_ROM) { | ||
935 | snd_printk ("sample slot %d " | ||
936 | "write protected\n", | ||
937 | header->number); | ||
938 | return -EACCES; | ||
939 | } | ||
940 | } | ||
941 | |||
942 | wavefront_delete_sample (dev, header->number); | ||
943 | } | ||
944 | |||
945 | if (header->size) { | ||
946 | dev->freemem = wavefront_freemem (dev); | ||
947 | |||
948 | if (dev->freemem < (int)header->size) { | ||
949 | snd_printk ("insufficient memory to " | ||
950 | "load %d byte sample.\n", | ||
951 | header->size); | ||
952 | return -ENOMEM; | ||
953 | } | ||
954 | |||
955 | } | ||
956 | |||
957 | skip = WF_GET_CHANNEL(&header->hdr.s); | ||
958 | |||
959 | if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { | ||
960 | snd_printk ("channel selection only " | ||
961 | "possible on 16-bit samples"); | ||
962 | return -(EINVAL); | ||
963 | } | ||
964 | |||
965 | switch (skip) { | ||
966 | case 0: | ||
967 | initial_skip = 0; | ||
968 | skip = 1; | ||
969 | break; | ||
970 | case 1: | ||
971 | initial_skip = 0; | ||
972 | skip = 2; | ||
973 | break; | ||
974 | case 2: | ||
975 | initial_skip = 1; | ||
976 | skip = 2; | ||
977 | break; | ||
978 | case 3: | ||
979 | initial_skip = 2; | ||
980 | skip = 3; | ||
981 | break; | ||
982 | case 4: | ||
983 | initial_skip = 3; | ||
984 | skip = 4; | ||
985 | break; | ||
986 | case 5: | ||
987 | initial_skip = 4; | ||
988 | skip = 5; | ||
989 | break; | ||
990 | case 6: | ||
991 | initial_skip = 5; | ||
992 | skip = 6; | ||
993 | break; | ||
994 | } | ||
995 | |||
996 | DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " | ||
997 | "initial skip = %d, skip = %d\n", | ||
998 | WF_GET_CHANNEL (&header->hdr.s), | ||
999 | initial_skip, skip); | ||
1000 | |||
1001 | /* Be safe, and zero the "Unused" bits ... */ | ||
1002 | |||
1003 | WF_SET_CHANNEL(&header->hdr.s, 0); | ||
1004 | |||
1005 | /* adjust size for 16 bit samples by dividing by two. We always | ||
1006 | send 16 bits per write, even for 8 bit samples, so the length | ||
1007 | is always half the size of the sample data in bytes. | ||
1008 | */ | ||
1009 | |||
1010 | length = header->size / 2; | ||
1011 | |||
1012 | /* the data we're sent has not been munged, and in fact, the | ||
1013 | header we have to send isn't just a munged copy either. | ||
1014 | so, build the sample header right here. | ||
1015 | */ | ||
1016 | |||
1017 | shptr = &sample_hdr[0]; | ||
1018 | |||
1019 | shptr = munge_int32 (header->number, shptr, 2); | ||
1020 | |||
1021 | if (header->size) { | ||
1022 | shptr = munge_int32 (length, shptr, 4); | ||
1023 | } | ||
1024 | |||
1025 | /* Yes, a 4 byte result doesn't contain all of the offset bits, | ||
1026 | but the offset only uses 24 bits. | ||
1027 | */ | ||
1028 | |||
1029 | shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleStartOffset), | ||
1030 | shptr, 4); | ||
1031 | shptr = munge_int32 (*((u32 *) &header->hdr.s.loopStartOffset), | ||
1032 | shptr, 4); | ||
1033 | shptr = munge_int32 (*((u32 *) &header->hdr.s.loopEndOffset), | ||
1034 | shptr, 4); | ||
1035 | shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleEndOffset), | ||
1036 | shptr, 4); | ||
1037 | |||
1038 | /* This one is truly weird. What kind of weirdo decided that in | ||
1039 | a system dominated by 16 and 32 bit integers, they would use | ||
1040 | a just 12 bits ? | ||
1041 | */ | ||
1042 | |||
1043 | shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); | ||
1044 | |||
1045 | /* Why is this nybblified, when the MSB is *always* zero ? | ||
1046 | Anyway, we can't take address of bitfield, so make a | ||
1047 | good-faith guess at where it starts. | ||
1048 | */ | ||
1049 | |||
1050 | shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), | ||
1051 | shptr, 2); | ||
1052 | |||
1053 | if (snd_wavefront_cmd (dev, | ||
1054 | header->size ? | ||
1055 | WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, | ||
1056 | NULL, sample_hdr)) { | ||
1057 | snd_printk ("sample %sdownload refused.\n", | ||
1058 | header->size ? "" : "header "); | ||
1059 | return -(EIO); | ||
1060 | } | ||
1061 | |||
1062 | if (header->size == 0) { | ||
1063 | goto sent; /* Sorry. Just had to have one somewhere */ | ||
1064 | } | ||
1065 | |||
1066 | data_end = dataptr + length; | ||
1067 | |||
1068 | /* Do any initial skip over an unused channel's data */ | ||
1069 | |||
1070 | dataptr += initial_skip; | ||
1071 | |||
1072 | for (written = 0, blocknum = 0; | ||
1073 | written < length; written += max_blksize, blocknum++) { | ||
1074 | |||
1075 | if ((length - written) > max_blksize) { | ||
1076 | blocksize = max_blksize; | ||
1077 | } else { | ||
1078 | /* round to nearest 16-byte value */ | ||
1079 | blocksize = ((length-written+7)&~0x7); | ||
1080 | } | ||
1081 | |||
1082 | if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) { | ||
1083 | snd_printk ("download block " | ||
1084 | "request refused.\n"); | ||
1085 | return -(EIO); | ||
1086 | } | ||
1087 | |||
1088 | for (i = 0; i < blocksize; i++) { | ||
1089 | |||
1090 | if (dataptr < data_end) { | ||
1091 | |||
1092 | __get_user (sample_short, dataptr); | ||
1093 | dataptr += skip; | ||
1094 | |||
1095 | if (data_is_unsigned) { /* GUS ? */ | ||
1096 | |||
1097 | if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { | ||
1098 | |||
1099 | /* 8 bit sample | ||
1100 | resolution, sign | ||
1101 | extend both bytes. | ||
1102 | */ | ||
1103 | |||
1104 | ((unsigned char*) | ||
1105 | &sample_short)[0] += 0x7f; | ||
1106 | ((unsigned char*) | ||
1107 | &sample_short)[1] += 0x7f; | ||
1108 | |||
1109 | } else { | ||
1110 | |||
1111 | /* 16 bit sample | ||
1112 | resolution, sign | ||
1113 | extend the MSB. | ||
1114 | */ | ||
1115 | |||
1116 | sample_short += 0x7fff; | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | } else { | ||
1121 | |||
1122 | /* In padding section of final block: | ||
1123 | |||
1124 | Don't fetch unsupplied data from | ||
1125 | user space, just continue with | ||
1126 | whatever the final value was. | ||
1127 | */ | ||
1128 | } | ||
1129 | |||
1130 | if (i < blocksize - 1) { | ||
1131 | outw (sample_short, dev->block_port); | ||
1132 | } else { | ||
1133 | outw (sample_short, dev->last_block_port); | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | /* Get "DMA page acknowledge", even though its really | ||
1138 | nothing to do with DMA at all. | ||
1139 | */ | ||
1140 | |||
1141 | if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) { | ||
1142 | if (dma_ack == -1) { | ||
1143 | snd_printk ("upload sample " | ||
1144 | "DMA ack timeout\n"); | ||
1145 | return -(EIO); | ||
1146 | } else { | ||
1147 | snd_printk ("upload sample " | ||
1148 | "DMA ack error 0x%x\n", | ||
1149 | dma_ack); | ||
1150 | return -(EIO); | ||
1151 | } | ||
1152 | } | ||
1153 | } | ||
1154 | |||
1155 | dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); | ||
1156 | |||
1157 | /* Note, label is here because sending the sample header shouldn't | ||
1158 | alter the sample_status info at all. | ||
1159 | */ | ||
1160 | |||
1161 | sent: | ||
1162 | return (0); | ||
1163 | } | ||
1164 | |||
1165 | static int | ||
1166 | wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) | ||
1167 | |||
1168 | { | ||
1169 | unsigned char alias_hdr[WF_ALIAS_BYTES]; | ||
1170 | |||
1171 | DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " | ||
1172 | "alias for %d\n", | ||
1173 | header->number, | ||
1174 | header->hdr.a.OriginalSample); | ||
1175 | |||
1176 | munge_int32 (header->number, &alias_hdr[0], 2); | ||
1177 | munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); | ||
1178 | munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), | ||
1179 | &alias_hdr[4], 4); | ||
1180 | munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), | ||
1181 | &alias_hdr[8], 4); | ||
1182 | munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), | ||
1183 | &alias_hdr[12], 4); | ||
1184 | munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), | ||
1185 | &alias_hdr[16], 4); | ||
1186 | munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); | ||
1187 | munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); | ||
1188 | |||
1189 | if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) { | ||
1190 | snd_printk ("download alias failed.\n"); | ||
1191 | return -(EIO); | ||
1192 | } | ||
1193 | |||
1194 | dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); | ||
1195 | |||
1196 | return (0); | ||
1197 | } | ||
1198 | |||
1199 | static int | ||
1200 | wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) | ||
1201 | { | ||
1202 | int i; | ||
1203 | int num_samples; | ||
1204 | unsigned char *msample_hdr; | ||
1205 | |||
1206 | msample_hdr = kmalloc(sizeof(WF_MSAMPLE_BYTES), GFP_KERNEL); | ||
1207 | if (! msample_hdr) | ||
1208 | return -ENOMEM; | ||
1209 | |||
1210 | munge_int32 (header->number, &msample_hdr[0], 2); | ||
1211 | |||
1212 | /* You'll recall at this point that the "number of samples" value | ||
1213 | in a wavefront_multisample struct is actually the log2 of the | ||
1214 | real number of samples. | ||
1215 | */ | ||
1216 | |||
1217 | num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); | ||
1218 | msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; | ||
1219 | |||
1220 | DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", | ||
1221 | header->number, | ||
1222 | header->hdr.ms.NumberOfSamples, | ||
1223 | num_samples); | ||
1224 | |||
1225 | for (i = 0; i < num_samples; i++) { | ||
1226 | DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", | ||
1227 | i, header->hdr.ms.SampleNumber[i]); | ||
1228 | munge_int32 (header->hdr.ms.SampleNumber[i], | ||
1229 | &msample_hdr[3+(i*2)], 2); | ||
1230 | } | ||
1231 | |||
1232 | /* Need a hack here to pass in the number of bytes | ||
1233 | to be written to the synth. This is ugly, and perhaps | ||
1234 | one day, I'll fix it. | ||
1235 | */ | ||
1236 | |||
1237 | if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE, | ||
1238 | (unsigned char *) (long) ((num_samples*2)+3), | ||
1239 | msample_hdr)) { | ||
1240 | snd_printk ("download of multisample failed.\n"); | ||
1241 | kfree(msample_hdr); | ||
1242 | return -(EIO); | ||
1243 | } | ||
1244 | |||
1245 | dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); | ||
1246 | |||
1247 | kfree(msample_hdr); | ||
1248 | return (0); | ||
1249 | } | ||
1250 | |||
1251 | static int | ||
1252 | wavefront_fetch_multisample (snd_wavefront_t *dev, | ||
1253 | wavefront_patch_info *header) | ||
1254 | { | ||
1255 | int i; | ||
1256 | unsigned char log_ns[1]; | ||
1257 | unsigned char number[2]; | ||
1258 | int num_samples; | ||
1259 | |||
1260 | munge_int32 (header->number, number, 2); | ||
1261 | |||
1262 | if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { | ||
1263 | snd_printk ("upload multisample failed.\n"); | ||
1264 | return -(EIO); | ||
1265 | } | ||
1266 | |||
1267 | DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", | ||
1268 | header->number, log_ns[0]); | ||
1269 | |||
1270 | header->hdr.ms.NumberOfSamples = log_ns[0]; | ||
1271 | |||
1272 | /* get the number of samples ... */ | ||
1273 | |||
1274 | num_samples = (1 << log_ns[0]); | ||
1275 | |||
1276 | for (i = 0; i < num_samples; i++) { | ||
1277 | char d[2]; | ||
1278 | int val; | ||
1279 | |||
1280 | if ((val = wavefront_read (dev)) == -1) { | ||
1281 | snd_printk ("upload multisample failed " | ||
1282 | "during sample loop.\n"); | ||
1283 | return -(EIO); | ||
1284 | } | ||
1285 | d[0] = val; | ||
1286 | |||
1287 | if ((val = wavefront_read (dev)) == -1) { | ||
1288 | snd_printk ("upload multisample failed " | ||
1289 | "during sample loop.\n"); | ||
1290 | return -(EIO); | ||
1291 | } | ||
1292 | d[1] = val; | ||
1293 | |||
1294 | header->hdr.ms.SampleNumber[i] = | ||
1295 | demunge_int32 ((unsigned char *) d, 2); | ||
1296 | |||
1297 | DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", | ||
1298 | i, header->hdr.ms.SampleNumber[i]); | ||
1299 | } | ||
1300 | |||
1301 | return (0); | ||
1302 | } | ||
1303 | |||
1304 | |||
1305 | static int | ||
1306 | wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header) | ||
1307 | |||
1308 | { | ||
1309 | unsigned char drumbuf[WF_DRUM_BYTES]; | ||
1310 | wavefront_drum *drum = &header->hdr.d; | ||
1311 | int i; | ||
1312 | |||
1313 | DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " | ||
1314 | "note %d, patch = %d\n", | ||
1315 | header->number, drum->PatchNumber); | ||
1316 | |||
1317 | drumbuf[0] = header->number & 0x7f; | ||
1318 | |||
1319 | for (i = 0; i < 4; i++) { | ||
1320 | munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); | ||
1321 | } | ||
1322 | |||
1323 | if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) { | ||
1324 | snd_printk ("download drum failed.\n"); | ||
1325 | return -(EIO); | ||
1326 | } | ||
1327 | |||
1328 | return (0); | ||
1329 | } | ||
1330 | |||
1331 | static int | ||
1332 | wavefront_find_free_sample (snd_wavefront_t *dev) | ||
1333 | |||
1334 | { | ||
1335 | int i; | ||
1336 | |||
1337 | for (i = 0; i < WF_MAX_SAMPLE; i++) { | ||
1338 | if (!(dev->sample_status[i] & WF_SLOT_FILLED)) { | ||
1339 | return i; | ||
1340 | } | ||
1341 | } | ||
1342 | snd_printk ("no free sample slots!\n"); | ||
1343 | return -1; | ||
1344 | } | ||
1345 | |||
1346 | #if 0 | ||
1347 | static int | ||
1348 | wavefront_find_free_patch (snd_wavefront_t *dev) | ||
1349 | |||
1350 | { | ||
1351 | int i; | ||
1352 | |||
1353 | for (i = 0; i < WF_MAX_PATCH; i++) { | ||
1354 | if (!(dev->patch_status[i] & WF_SLOT_FILLED)) { | ||
1355 | return i; | ||
1356 | } | ||
1357 | } | ||
1358 | snd_printk ("no free patch slots!\n"); | ||
1359 | return -1; | ||
1360 | } | ||
1361 | #endif | ||
1362 | |||
1363 | static int | ||
1364 | wavefront_load_patch (snd_wavefront_t *dev, const char __user *addr) | ||
1365 | { | ||
1366 | wavefront_patch_info *header; | ||
1367 | int err; | ||
1368 | |||
1369 | header = kmalloc(sizeof(*header), GFP_KERNEL); | ||
1370 | if (! header) | ||
1371 | return -ENOMEM; | ||
1372 | |||
1373 | if (copy_from_user (header, addr, sizeof(wavefront_patch_info) - | ||
1374 | sizeof(wavefront_any))) { | ||
1375 | snd_printk ("bad address for load patch.\n"); | ||
1376 | err = -EFAULT; | ||
1377 | goto __error; | ||
1378 | } | ||
1379 | |||
1380 | DPRINT (WF_DEBUG_LOAD_PATCH, "download " | ||
1381 | "Sample type: %d " | ||
1382 | "Sample number: %d " | ||
1383 | "Sample size: %d\n", | ||
1384 | header->subkey, | ||
1385 | header->number, | ||
1386 | header->size); | ||
1387 | |||
1388 | switch (header->subkey) { | ||
1389 | case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ | ||
1390 | |||
1391 | if (copy_from_user (&header->hdr.s, header->hdrptr, | ||
1392 | sizeof (wavefront_sample))) { | ||
1393 | err = -EFAULT; | ||
1394 | break; | ||
1395 | } | ||
1396 | |||
1397 | err = wavefront_send_sample (dev, header, header->dataptr, 0); | ||
1398 | break; | ||
1399 | |||
1400 | case WF_ST_MULTISAMPLE: | ||
1401 | |||
1402 | if (copy_from_user (&header->hdr.s, header->hdrptr, | ||
1403 | sizeof (wavefront_multisample))) { | ||
1404 | err = -EFAULT; | ||
1405 | break; | ||
1406 | } | ||
1407 | |||
1408 | err = wavefront_send_multisample (dev, header); | ||
1409 | break; | ||
1410 | |||
1411 | case WF_ST_ALIAS: | ||
1412 | |||
1413 | if (copy_from_user (&header->hdr.a, header->hdrptr, | ||
1414 | sizeof (wavefront_alias))) { | ||
1415 | err = -EFAULT; | ||
1416 | break; | ||
1417 | } | ||
1418 | |||
1419 | err = wavefront_send_alias (dev, header); | ||
1420 | break; | ||
1421 | |||
1422 | case WF_ST_DRUM: | ||
1423 | if (copy_from_user (&header->hdr.d, header->hdrptr, | ||
1424 | sizeof (wavefront_drum))) { | ||
1425 | err = -EFAULT; | ||
1426 | break; | ||
1427 | } | ||
1428 | |||
1429 | err = wavefront_send_drum (dev, header); | ||
1430 | break; | ||
1431 | |||
1432 | case WF_ST_PATCH: | ||
1433 | if (copy_from_user (&header->hdr.p, header->hdrptr, | ||
1434 | sizeof (wavefront_patch))) { | ||
1435 | err = -EFAULT; | ||
1436 | break; | ||
1437 | } | ||
1438 | |||
1439 | err = wavefront_send_patch (dev, header); | ||
1440 | break; | ||
1441 | |||
1442 | case WF_ST_PROGRAM: | ||
1443 | if (copy_from_user (&header->hdr.pr, header->hdrptr, | ||
1444 | sizeof (wavefront_program))) { | ||
1445 | err = -EFAULT; | ||
1446 | break; | ||
1447 | } | ||
1448 | |||
1449 | err = wavefront_send_program (dev, header); | ||
1450 | break; | ||
1451 | |||
1452 | default: | ||
1453 | snd_printk ("unknown patch type %d.\n", | ||
1454 | header->subkey); | ||
1455 | err = -EINVAL; | ||
1456 | break; | ||
1457 | } | ||
1458 | |||
1459 | __error: | ||
1460 | kfree(header); | ||
1461 | return err; | ||
1462 | } | ||
1463 | |||
1464 | /*********************************************************************** | ||
1465 | WaveFront: hardware-dependent interface | ||
1466 | ***********************************************************************/ | ||
1467 | |||
1468 | static void | ||
1469 | process_sample_hdr (u8 *buf) | ||
1470 | |||
1471 | { | ||
1472 | wavefront_sample s; | ||
1473 | u8 *ptr; | ||
1474 | |||
1475 | ptr = buf; | ||
1476 | |||
1477 | /* The board doesn't send us an exact copy of a "wavefront_sample" | ||
1478 | in response to an Upload Sample Header command. Instead, we | ||
1479 | have to convert the data format back into our data structure, | ||
1480 | just as in the Download Sample command, where we have to do | ||
1481 | something very similar in the reverse direction. | ||
1482 | */ | ||
1483 | |||
1484 | *((u32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; | ||
1485 | *((u32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; | ||
1486 | *((u32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; | ||
1487 | *((u32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; | ||
1488 | *((u32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; | ||
1489 | |||
1490 | s.SampleResolution = *ptr & 0x3; | ||
1491 | s.Loop = *ptr & 0x8; | ||
1492 | s.Bidirectional = *ptr & 0x10; | ||
1493 | s.Reverse = *ptr & 0x40; | ||
1494 | |||
1495 | /* Now copy it back to where it came from */ | ||
1496 | |||
1497 | memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); | ||
1498 | } | ||
1499 | |||
1500 | static int | ||
1501 | wavefront_synth_control (snd_wavefront_card_t *acard, | ||
1502 | wavefront_control *wc) | ||
1503 | |||
1504 | { | ||
1505 | snd_wavefront_t *dev = &acard->wavefront; | ||
1506 | unsigned char patchnumbuf[2]; | ||
1507 | int i; | ||
1508 | |||
1509 | DPRINT (WF_DEBUG_CMD, "synth control with " | ||
1510 | "cmd 0x%x\n", wc->cmd); | ||
1511 | |||
1512 | /* Pre-handling of or for various commands */ | ||
1513 | |||
1514 | switch (wc->cmd) { | ||
1515 | |||
1516 | case WFC_DISABLE_INTERRUPTS: | ||
1517 | snd_printk ("interrupts disabled.\n"); | ||
1518 | outb (0x80|0x20, dev->control_port); | ||
1519 | dev->interrupts_are_midi = 1; | ||
1520 | return 0; | ||
1521 | |||
1522 | case WFC_ENABLE_INTERRUPTS: | ||
1523 | snd_printk ("interrupts enabled.\n"); | ||
1524 | outb (0x80|0x40|0x20, dev->control_port); | ||
1525 | dev->interrupts_are_midi = 1; | ||
1526 | return 0; | ||
1527 | |||
1528 | case WFC_INTERRUPT_STATUS: | ||
1529 | wc->rbuf[0] = dev->interrupts_are_midi; | ||
1530 | return 0; | ||
1531 | |||
1532 | case WFC_ROMSAMPLES_RDONLY: | ||
1533 | dev->rom_samples_rdonly = wc->wbuf[0]; | ||
1534 | wc->status = 0; | ||
1535 | return 0; | ||
1536 | |||
1537 | case WFC_IDENTIFY_SLOT_TYPE: | ||
1538 | i = wc->wbuf[0] | (wc->wbuf[1] << 7); | ||
1539 | if (i <0 || i >= WF_MAX_SAMPLE) { | ||
1540 | snd_printk ("invalid slot ID %d\n", | ||
1541 | i); | ||
1542 | wc->status = EINVAL; | ||
1543 | return -EINVAL; | ||
1544 | } | ||
1545 | wc->rbuf[0] = dev->sample_status[i]; | ||
1546 | wc->status = 0; | ||
1547 | return 0; | ||
1548 | |||
1549 | case WFC_DEBUG_DRIVER: | ||
1550 | dev->debug = wc->wbuf[0]; | ||
1551 | snd_printk ("debug = 0x%x\n", dev->debug); | ||
1552 | return 0; | ||
1553 | |||
1554 | case WFC_UPLOAD_PATCH: | ||
1555 | munge_int32 (*((u32 *) wc->wbuf), patchnumbuf, 2); | ||
1556 | memcpy (wc->wbuf, patchnumbuf, 2); | ||
1557 | break; | ||
1558 | |||
1559 | case WFC_UPLOAD_MULTISAMPLE: | ||
1560 | /* multisamples have to be handled differently, and | ||
1561 | cannot be dealt with properly by snd_wavefront_cmd() alone. | ||
1562 | */ | ||
1563 | wc->status = wavefront_fetch_multisample | ||
1564 | (dev, (wavefront_patch_info *) wc->rbuf); | ||
1565 | return 0; | ||
1566 | |||
1567 | case WFC_UPLOAD_SAMPLE_ALIAS: | ||
1568 | snd_printk ("support for sample alias upload " | ||
1569 | "being considered.\n"); | ||
1570 | wc->status = EINVAL; | ||
1571 | return -EINVAL; | ||
1572 | } | ||
1573 | |||
1574 | wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf); | ||
1575 | |||
1576 | /* Post-handling of certain commands. | ||
1577 | |||
1578 | In particular, if the command was an upload, demunge the data | ||
1579 | so that the user-level doesn't have to think about it. | ||
1580 | */ | ||
1581 | |||
1582 | if (wc->status == 0) { | ||
1583 | switch (wc->cmd) { | ||
1584 | /* intercept any freemem requests so that we know | ||
1585 | we are always current with the user-level view | ||
1586 | of things. | ||
1587 | */ | ||
1588 | |||
1589 | case WFC_REPORT_FREE_MEMORY: | ||
1590 | dev->freemem = demunge_int32 (wc->rbuf, 4); | ||
1591 | break; | ||
1592 | |||
1593 | case WFC_UPLOAD_PATCH: | ||
1594 | demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); | ||
1595 | break; | ||
1596 | |||
1597 | case WFC_UPLOAD_PROGRAM: | ||
1598 | demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); | ||
1599 | break; | ||
1600 | |||
1601 | case WFC_UPLOAD_EDRUM_PROGRAM: | ||
1602 | demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); | ||
1603 | break; | ||
1604 | |||
1605 | case WFC_UPLOAD_SAMPLE_HEADER: | ||
1606 | process_sample_hdr (wc->rbuf); | ||
1607 | break; | ||
1608 | |||
1609 | case WFC_UPLOAD_SAMPLE_ALIAS: | ||
1610 | snd_printk ("support for " | ||
1611 | "sample aliases still " | ||
1612 | "being considered.\n"); | ||
1613 | break; | ||
1614 | |||
1615 | case WFC_VMIDI_OFF: | ||
1616 | snd_wavefront_midi_disable_virtual (acard); | ||
1617 | break; | ||
1618 | |||
1619 | case WFC_VMIDI_ON: | ||
1620 | snd_wavefront_midi_enable_virtual (acard); | ||
1621 | break; | ||
1622 | } | ||
1623 | } | ||
1624 | |||
1625 | return 0; | ||
1626 | } | ||
1627 | |||
1628 | int | ||
1629 | snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file) | ||
1630 | |||
1631 | { | ||
1632 | if (!try_module_get(hw->card->module)) | ||
1633 | return -EFAULT; | ||
1634 | file->private_data = hw; | ||
1635 | return 0; | ||
1636 | } | ||
1637 | |||
1638 | int | ||
1639 | snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file) | ||
1640 | |||
1641 | { | ||
1642 | module_put(hw->card->module); | ||
1643 | return 0; | ||
1644 | } | ||
1645 | |||
1646 | int | ||
1647 | snd_wavefront_synth_ioctl (snd_hwdep_t *hw, struct file *file, | ||
1648 | unsigned int cmd, unsigned long arg) | ||
1649 | |||
1650 | { | ||
1651 | snd_card_t *card; | ||
1652 | snd_wavefront_t *dev; | ||
1653 | snd_wavefront_card_t *acard; | ||
1654 | wavefront_control *wc; | ||
1655 | void __user *argp = (void __user *)arg; | ||
1656 | int err; | ||
1657 | |||
1658 | card = (snd_card_t *) hw->card; | ||
1659 | |||
1660 | snd_assert(card != NULL, return -ENODEV); | ||
1661 | |||
1662 | snd_assert(card->private_data != NULL, return -ENODEV); | ||
1663 | |||
1664 | acard = card->private_data; | ||
1665 | dev = &acard->wavefront; | ||
1666 | |||
1667 | switch (cmd) { | ||
1668 | case WFCTL_LOAD_SPP: | ||
1669 | if (wavefront_load_patch (dev, argp) != 0) { | ||
1670 | return -EIO; | ||
1671 | } | ||
1672 | break; | ||
1673 | |||
1674 | case WFCTL_WFCMD: | ||
1675 | wc = kmalloc(sizeof(*wc), GFP_KERNEL); | ||
1676 | if (! wc) | ||
1677 | return -ENOMEM; | ||
1678 | if (copy_from_user (wc, argp, sizeof (*wc))) | ||
1679 | err = -EFAULT; | ||
1680 | else if (wavefront_synth_control (acard, wc) < 0) | ||
1681 | err = -EIO; | ||
1682 | else if (copy_to_user (argp, wc, sizeof (*wc))) | ||
1683 | err = -EFAULT; | ||
1684 | else | ||
1685 | err = 0; | ||
1686 | kfree(wc); | ||
1687 | return err; | ||
1688 | |||
1689 | default: | ||
1690 | return -EINVAL; | ||
1691 | } | ||
1692 | |||
1693 | return 0; | ||
1694 | } | ||
1695 | |||
1696 | |||
1697 | /***********************************************************************/ | ||
1698 | /* WaveFront: interface for card-level wavefront module */ | ||
1699 | /***********************************************************************/ | ||
1700 | |||
1701 | void | ||
1702 | snd_wavefront_internal_interrupt (snd_wavefront_card_t *card) | ||
1703 | { | ||
1704 | snd_wavefront_t *dev = &card->wavefront; | ||
1705 | |||
1706 | /* | ||
1707 | Some comments on interrupts. I attempted a version of this | ||
1708 | driver that used interrupts throughout the code instead of | ||
1709 | doing busy and/or sleep-waiting. Alas, it appears that once | ||
1710 | the Motorola firmware is downloaded, the card *never* | ||
1711 | generates an RX interrupt. These are successfully generated | ||
1712 | during firmware loading, and after that wavefront_status() | ||
1713 | reports that an interrupt is pending on the card from time | ||
1714 | to time, but it never seems to be delivered to this | ||
1715 | driver. Note also that wavefront_status() continues to | ||
1716 | report that RX interrupts are enabled, suggesting that I | ||
1717 | didn't goof up and disable them by mistake. | ||
1718 | |||
1719 | Thus, I stepped back to a prior version of | ||
1720 | wavefront_wait(), the only place where this really | ||
1721 | matters. Its sad, but I've looked through the code to check | ||
1722 | on things, and I really feel certain that the Motorola | ||
1723 | firmware prevents RX-ready interrupts. | ||
1724 | */ | ||
1725 | |||
1726 | if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { | ||
1727 | return; | ||
1728 | } | ||
1729 | |||
1730 | spin_lock(&dev->irq_lock); | ||
1731 | dev->irq_ok = 1; | ||
1732 | dev->irq_cnt++; | ||
1733 | spin_unlock(&dev->irq_lock); | ||
1734 | wake_up(&dev->interrupt_sleeper); | ||
1735 | } | ||
1736 | |||
1737 | /* STATUS REGISTER | ||
1738 | |||
1739 | 0 Host Rx Interrupt Enable (1=Enabled) | ||
1740 | 1 Host Rx Register Full (1=Full) | ||
1741 | 2 Host Rx Interrupt Pending (1=Interrupt) | ||
1742 | 3 Unused | ||
1743 | 4 Host Tx Interrupt (1=Enabled) | ||
1744 | 5 Host Tx Register empty (1=Empty) | ||
1745 | 6 Host Tx Interrupt Pending (1=Interrupt) | ||
1746 | 7 Unused | ||
1747 | */ | ||
1748 | |||
1749 | static int __init | ||
1750 | snd_wavefront_interrupt_bits (int irq) | ||
1751 | |||
1752 | { | ||
1753 | int bits; | ||
1754 | |||
1755 | switch (irq) { | ||
1756 | case 9: | ||
1757 | bits = 0x00; | ||
1758 | break; | ||
1759 | case 5: | ||
1760 | bits = 0x08; | ||
1761 | break; | ||
1762 | case 12: | ||
1763 | bits = 0x10; | ||
1764 | break; | ||
1765 | case 15: | ||
1766 | bits = 0x18; | ||
1767 | break; | ||
1768 | |||
1769 | default: | ||
1770 | snd_printk ("invalid IRQ %d\n", irq); | ||
1771 | bits = -1; | ||
1772 | } | ||
1773 | |||
1774 | return bits; | ||
1775 | } | ||
1776 | |||
1777 | static void __init | ||
1778 | wavefront_should_cause_interrupt (snd_wavefront_t *dev, | ||
1779 | int val, int port, int timeout) | ||
1780 | |||
1781 | { | ||
1782 | wait_queue_t wait; | ||
1783 | |||
1784 | init_waitqueue_entry(&wait, current); | ||
1785 | spin_lock_irq(&dev->irq_lock); | ||
1786 | add_wait_queue(&dev->interrupt_sleeper, &wait); | ||
1787 | dev->irq_ok = 0; | ||
1788 | outb (val,port); | ||
1789 | spin_unlock_irq(&dev->irq_lock); | ||
1790 | while (1) { | ||
1791 | set_current_state(TASK_INTERRUPTIBLE); | ||
1792 | if ((timeout = schedule_timeout(timeout)) == 0) | ||
1793 | return; | ||
1794 | if (dev->irq_ok) | ||
1795 | return; | ||
1796 | } | ||
1797 | } | ||
1798 | |||
1799 | static int __init | ||
1800 | wavefront_reset_to_cleanliness (snd_wavefront_t *dev) | ||
1801 | |||
1802 | { | ||
1803 | int bits; | ||
1804 | int hwv[2]; | ||
1805 | |||
1806 | /* IRQ already checked */ | ||
1807 | |||
1808 | bits = snd_wavefront_interrupt_bits (dev->irq); | ||
1809 | |||
1810 | /* try reset of port */ | ||
1811 | |||
1812 | outb (0x0, dev->control_port); | ||
1813 | |||
1814 | /* At this point, the board is in reset, and the H/W initialization | ||
1815 | register is accessed at the same address as the data port. | ||
1816 | |||
1817 | Bit 7 - Enable IRQ Driver | ||
1818 | 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs | ||
1819 | 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. | ||
1820 | |||
1821 | Bit 6 - MIDI Interface Select | ||
1822 | |||
1823 | 0 - Use the MIDI Input from the 26-pin WaveBlaster | ||
1824 | compatible header as the serial MIDI source | ||
1825 | 1 - Use the MIDI Input from the 9-pin D connector as the | ||
1826 | serial MIDI source. | ||
1827 | |||
1828 | Bits 5:3 - IRQ Selection | ||
1829 | 0 0 0 - IRQ 2/9 | ||
1830 | 0 0 1 - IRQ 5 | ||
1831 | 0 1 0 - IRQ 12 | ||
1832 | 0 1 1 - IRQ 15 | ||
1833 | 1 0 0 - Reserved | ||
1834 | 1 0 1 - Reserved | ||
1835 | 1 1 0 - Reserved | ||
1836 | 1 1 1 - Reserved | ||
1837 | |||
1838 | Bits 2:1 - Reserved | ||
1839 | Bit 0 - Disable Boot ROM | ||
1840 | 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM | ||
1841 | 1 - memory accesses to 03FC30-03FFFFH are directed to external | ||
1842 | storage. | ||
1843 | |||
1844 | */ | ||
1845 | |||
1846 | /* configure hardware: IRQ, enable interrupts, | ||
1847 | plus external 9-pin MIDI interface selected | ||
1848 | */ | ||
1849 | |||
1850 | outb (0x80 | 0x40 | bits, dev->data_port); | ||
1851 | |||
1852 | /* CONTROL REGISTER | ||
1853 | |||
1854 | 0 Host Rx Interrupt Enable (1=Enabled) 0x1 | ||
1855 | 1 Unused 0x2 | ||
1856 | 2 Unused 0x4 | ||
1857 | 3 Unused 0x8 | ||
1858 | 4 Host Tx Interrupt Enable 0x10 | ||
1859 | 5 Mute (0=Mute; 1=Play) 0x20 | ||
1860 | 6 Master Interrupt Enable (1=Enabled) 0x40 | ||
1861 | 7 Master Reset (0=Reset; 1=Run) 0x80 | ||
1862 | |||
1863 | Take us out of reset, mute output, master + TX + RX interrupts on. | ||
1864 | |||
1865 | We'll get an interrupt presumably to tell us that the TX | ||
1866 | register is clear. | ||
1867 | */ | ||
1868 | |||
1869 | wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1, | ||
1870 | dev->control_port, | ||
1871 | (reset_time*HZ)/100); | ||
1872 | |||
1873 | /* Note: data port is now the data port, not the h/w initialization | ||
1874 | port. | ||
1875 | */ | ||
1876 | |||
1877 | if (!dev->irq_ok) { | ||
1878 | snd_printk ("intr not received after h/w un-reset.\n"); | ||
1879 | goto gone_bad; | ||
1880 | } | ||
1881 | |||
1882 | /* Note: data port is now the data port, not the h/w initialization | ||
1883 | port. | ||
1884 | |||
1885 | At this point, only "HW VERSION" or "DOWNLOAD OS" commands | ||
1886 | will work. So, issue one of them, and wait for TX | ||
1887 | interrupt. This can take a *long* time after a cold boot, | ||
1888 | while the ISC ROM does its RAM test. The SDK says up to 4 | ||
1889 | seconds - with 12MB of RAM on a Tropez+, it takes a lot | ||
1890 | longer than that (~16secs). Note that the card understands | ||
1891 | the difference between a warm and a cold boot, so | ||
1892 | subsequent ISC2115 reboots (say, caused by module | ||
1893 | reloading) will get through this much faster. | ||
1894 | |||
1895 | XXX Interesting question: why is no RX interrupt received first ? | ||
1896 | */ | ||
1897 | |||
1898 | wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, | ||
1899 | dev->data_port, ramcheck_time*HZ); | ||
1900 | |||
1901 | if (!dev->irq_ok) { | ||
1902 | snd_printk ("post-RAM-check interrupt not received.\n"); | ||
1903 | goto gone_bad; | ||
1904 | } | ||
1905 | |||
1906 | if (!wavefront_wait (dev, STAT_CAN_READ)) { | ||
1907 | snd_printk ("no response to HW version cmd.\n"); | ||
1908 | goto gone_bad; | ||
1909 | } | ||
1910 | |||
1911 | if ((hwv[0] = wavefront_read (dev)) == -1) { | ||
1912 | snd_printk ("board not responding correctly.\n"); | ||
1913 | goto gone_bad; | ||
1914 | } | ||
1915 | |||
1916 | if (hwv[0] == 0xFF) { /* NAK */ | ||
1917 | |||
1918 | /* Board's RAM test failed. Try to read error code, | ||
1919 | and tell us about it either way. | ||
1920 | */ | ||
1921 | |||
1922 | if ((hwv[0] = wavefront_read (dev)) == -1) { | ||
1923 | snd_printk ("on-board RAM test failed " | ||
1924 | "(bad error code).\n"); | ||
1925 | } else { | ||
1926 | snd_printk ("on-board RAM test failed " | ||
1927 | "(error code: 0x%x).\n", | ||
1928 | hwv[0]); | ||
1929 | } | ||
1930 | goto gone_bad; | ||
1931 | } | ||
1932 | |||
1933 | /* We're OK, just get the next byte of the HW version response */ | ||
1934 | |||
1935 | if ((hwv[1] = wavefront_read (dev)) == -1) { | ||
1936 | snd_printk ("incorrect h/w response.\n"); | ||
1937 | goto gone_bad; | ||
1938 | } | ||
1939 | |||
1940 | snd_printk ("hardware version %d.%d\n", | ||
1941 | hwv[0], hwv[1]); | ||
1942 | |||
1943 | return 0; | ||
1944 | |||
1945 | |||
1946 | gone_bad: | ||
1947 | return (1); | ||
1948 | } | ||
1949 | |||
1950 | #include <linux/fs.h> | ||
1951 | #include <linux/mm.h> | ||
1952 | #include <linux/slab.h> | ||
1953 | #include <linux/unistd.h> | ||
1954 | #include <linux/syscalls.h> | ||
1955 | #include <asm/uaccess.h> | ||
1956 | |||
1957 | |||
1958 | static int __init | ||
1959 | wavefront_download_firmware (snd_wavefront_t *dev, char *path) | ||
1960 | |||
1961 | { | ||
1962 | unsigned char section[WF_SECTION_MAX]; | ||
1963 | signed char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ | ||
1964 | int section_cnt_downloaded = 0; | ||
1965 | int fd; | ||
1966 | int c; | ||
1967 | int i; | ||
1968 | mm_segment_t fs; | ||
1969 | |||
1970 | /* This tries to be a bit cleverer than the stuff Alan Cox did for | ||
1971 | the generic sound firmware, in that it actually knows | ||
1972 | something about the structure of the Motorola firmware. In | ||
1973 | particular, it uses a version that has been stripped of the | ||
1974 | 20K of useless header information, and had section lengths | ||
1975 | added, making it possible to load the entire OS without any | ||
1976 | [kv]malloc() activity, since the longest entity we ever read is | ||
1977 | 42 bytes (well, WF_SECTION_MAX) long. | ||
1978 | */ | ||
1979 | |||
1980 | fs = get_fs(); | ||
1981 | set_fs (get_ds()); | ||
1982 | |||
1983 | if ((fd = sys_open ((char __user *) path, 0, 0)) < 0) { | ||
1984 | snd_printk ("Unable to load \"%s\".\n", | ||
1985 | path); | ||
1986 | return 1; | ||
1987 | } | ||
1988 | |||
1989 | while (1) { | ||
1990 | int x; | ||
1991 | |||
1992 | if ((x = sys_read (fd, (char __user *) §ion_length, sizeof (section_length))) != | ||
1993 | sizeof (section_length)) { | ||
1994 | snd_printk ("firmware read error.\n"); | ||
1995 | goto failure; | ||
1996 | } | ||
1997 | |||
1998 | if (section_length == 0) { | ||
1999 | break; | ||
2000 | } | ||
2001 | |||
2002 | if (section_length < 0 || section_length > WF_SECTION_MAX) { | ||
2003 | snd_printk ("invalid firmware section length %d\n", | ||
2004 | section_length); | ||
2005 | goto failure; | ||
2006 | } | ||
2007 | |||
2008 | if (sys_read (fd, (char __user *) section, section_length) != section_length) { | ||
2009 | snd_printk ("firmware section " | ||
2010 | "read error.\n"); | ||
2011 | goto failure; | ||
2012 | } | ||
2013 | |||
2014 | /* Send command */ | ||
2015 | |||
2016 | if (wavefront_write (dev, WFC_DOWNLOAD_OS)) { | ||
2017 | goto failure; | ||
2018 | } | ||
2019 | |||
2020 | for (i = 0; i < section_length; i++) { | ||
2021 | if (wavefront_write (dev, section[i])) { | ||
2022 | goto failure; | ||
2023 | } | ||
2024 | } | ||
2025 | |||
2026 | /* get ACK */ | ||
2027 | |||
2028 | if (wavefront_wait (dev, STAT_CAN_READ)) { | ||
2029 | |||
2030 | if ((c = inb (dev->data_port)) != WF_ACK) { | ||
2031 | |||
2032 | snd_printk ("download " | ||
2033 | "of section #%d not " | ||
2034 | "acknowledged, ack = 0x%x\n", | ||
2035 | section_cnt_downloaded + 1, c); | ||
2036 | goto failure; | ||
2037 | |||
2038 | } | ||
2039 | |||
2040 | } else { | ||
2041 | snd_printk ("time out for firmware ACK.\n"); | ||
2042 | goto failure; | ||
2043 | } | ||
2044 | |||
2045 | } | ||
2046 | |||
2047 | sys_close (fd); | ||
2048 | set_fs (fs); | ||
2049 | return 0; | ||
2050 | |||
2051 | failure: | ||
2052 | sys_close (fd); | ||
2053 | set_fs (fs); | ||
2054 | snd_printk ("firmware download failed!!!\n"); | ||
2055 | return 1; | ||
2056 | } | ||
2057 | |||
2058 | |||
2059 | static int __init | ||
2060 | wavefront_do_reset (snd_wavefront_t *dev) | ||
2061 | |||
2062 | { | ||
2063 | char voices[1]; | ||
2064 | |||
2065 | if (wavefront_reset_to_cleanliness (dev)) { | ||
2066 | snd_printk ("hw reset failed.\n"); | ||
2067 | goto gone_bad; | ||
2068 | } | ||
2069 | |||
2070 | if (dev->israw) { | ||
2071 | if (wavefront_download_firmware (dev, ospath)) { | ||
2072 | goto gone_bad; | ||
2073 | } | ||
2074 | |||
2075 | dev->israw = 0; | ||
2076 | |||
2077 | /* Wait for the OS to get running. The protocol for | ||
2078 | this is non-obvious, and was determined by | ||
2079 | using port-IO tracing in DOSemu and some | ||
2080 | experimentation here. | ||
2081 | |||
2082 | Rather than using timed waits, use interrupts creatively. | ||
2083 | */ | ||
2084 | |||
2085 | wavefront_should_cause_interrupt (dev, WFC_NOOP, | ||
2086 | dev->data_port, | ||
2087 | (osrun_time*HZ)); | ||
2088 | |||
2089 | if (!dev->irq_ok) { | ||
2090 | snd_printk ("no post-OS interrupt.\n"); | ||
2091 | goto gone_bad; | ||
2092 | } | ||
2093 | |||
2094 | /* Now, do it again ! */ | ||
2095 | |||
2096 | wavefront_should_cause_interrupt (dev, WFC_NOOP, | ||
2097 | dev->data_port, (10*HZ)); | ||
2098 | |||
2099 | if (!dev->irq_ok) { | ||
2100 | snd_printk ("no post-OS interrupt(2).\n"); | ||
2101 | goto gone_bad; | ||
2102 | } | ||
2103 | |||
2104 | /* OK, no (RX/TX) interrupts any more, but leave mute | ||
2105 | in effect. | ||
2106 | */ | ||
2107 | |||
2108 | outb (0x80|0x40, dev->control_port); | ||
2109 | } | ||
2110 | |||
2111 | /* SETUPSND.EXE asks for sample memory config here, but since i | ||
2112 | have no idea how to interpret the result, we'll forget | ||
2113 | about it. | ||
2114 | */ | ||
2115 | |||
2116 | if ((dev->freemem = wavefront_freemem (dev)) < 0) { | ||
2117 | goto gone_bad; | ||
2118 | } | ||
2119 | |||
2120 | snd_printk ("available DRAM %dk\n", dev->freemem / 1024); | ||
2121 | |||
2122 | if (wavefront_write (dev, 0xf0) || | ||
2123 | wavefront_write (dev, 1) || | ||
2124 | (wavefront_read (dev) < 0)) { | ||
2125 | dev->debug = 0; | ||
2126 | snd_printk ("MPU emulation mode not set.\n"); | ||
2127 | goto gone_bad; | ||
2128 | } | ||
2129 | |||
2130 | voices[0] = 32; | ||
2131 | |||
2132 | if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, NULL, voices)) { | ||
2133 | snd_printk ("cannot set number of voices to 32.\n"); | ||
2134 | goto gone_bad; | ||
2135 | } | ||
2136 | |||
2137 | |||
2138 | return 0; | ||
2139 | |||
2140 | gone_bad: | ||
2141 | /* reset that sucker so that it doesn't bother us. */ | ||
2142 | |||
2143 | outb (0x0, dev->control_port); | ||
2144 | dev->interrupts_are_midi = 0; | ||
2145 | return 1; | ||
2146 | } | ||
2147 | |||
2148 | int __init | ||
2149 | snd_wavefront_start (snd_wavefront_t *dev) | ||
2150 | |||
2151 | { | ||
2152 | int samples_are_from_rom; | ||
2153 | |||
2154 | /* IMPORTANT: assumes that snd_wavefront_detect() and/or | ||
2155 | wavefront_reset_to_cleanliness() has already been called | ||
2156 | */ | ||
2157 | |||
2158 | if (dev->israw) { | ||
2159 | samples_are_from_rom = 1; | ||
2160 | } else { | ||
2161 | /* XXX is this always true ? */ | ||
2162 | samples_are_from_rom = 0; | ||
2163 | } | ||
2164 | |||
2165 | if (dev->israw || fx_raw) { | ||
2166 | if (wavefront_do_reset (dev)) { | ||
2167 | return -1; | ||
2168 | } | ||
2169 | } | ||
2170 | /* Check for FX device, present only on Tropez+ */ | ||
2171 | |||
2172 | dev->has_fx = (snd_wavefront_fx_detect (dev) == 0); | ||
2173 | |||
2174 | if (dev->has_fx && fx_raw) { | ||
2175 | snd_wavefront_fx_start (dev); | ||
2176 | } | ||
2177 | |||
2178 | wavefront_get_sample_status (dev, samples_are_from_rom); | ||
2179 | wavefront_get_program_status (dev); | ||
2180 | wavefront_get_patch_status (dev); | ||
2181 | |||
2182 | /* Start normal operation: unreset, master interrupt enabled, no mute | ||
2183 | */ | ||
2184 | |||
2185 | outb (0x80|0x40|0x20, dev->control_port); | ||
2186 | |||
2187 | return (0); | ||
2188 | } | ||
2189 | |||
2190 | int __init | ||
2191 | snd_wavefront_detect (snd_wavefront_card_t *card) | ||
2192 | |||
2193 | { | ||
2194 | unsigned char rbuf[4], wbuf[4]; | ||
2195 | snd_wavefront_t *dev = &card->wavefront; | ||
2196 | |||
2197 | /* returns zero if a WaveFront card is successfully detected. | ||
2198 | negative otherwise. | ||
2199 | */ | ||
2200 | |||
2201 | dev->israw = 0; | ||
2202 | dev->has_fx = 0; | ||
2203 | dev->debug = debug_default; | ||
2204 | dev->interrupts_are_midi = 0; | ||
2205 | dev->irq_cnt = 0; | ||
2206 | dev->rom_samples_rdonly = 1; | ||
2207 | |||
2208 | if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { | ||
2209 | |||
2210 | dev->fw_version[0] = rbuf[0]; | ||
2211 | dev->fw_version[1] = rbuf[1]; | ||
2212 | |||
2213 | snd_printk ("firmware %d.%d already loaded.\n", | ||
2214 | rbuf[0], rbuf[1]); | ||
2215 | |||
2216 | /* check that a command actually works */ | ||
2217 | |||
2218 | if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION, | ||
2219 | rbuf, wbuf) == 0) { | ||
2220 | dev->hw_version[0] = rbuf[0]; | ||
2221 | dev->hw_version[1] = rbuf[1]; | ||
2222 | } else { | ||
2223 | snd_printk ("not raw, but no " | ||
2224 | "hardware version!\n"); | ||
2225 | return -1; | ||
2226 | } | ||
2227 | |||
2228 | if (!wf_raw) { | ||
2229 | return 0; | ||
2230 | } else { | ||
2231 | snd_printk ("reloading firmware as you requested.\n"); | ||
2232 | dev->israw = 1; | ||
2233 | } | ||
2234 | |||
2235 | } else { | ||
2236 | |||
2237 | dev->israw = 1; | ||
2238 | snd_printk ("no response to firmware probe, assume raw.\n"); | ||
2239 | |||
2240 | } | ||
2241 | |||
2242 | return 0; | ||
2243 | } | ||