diff options
Diffstat (limited to 'sound/oss/cs4232.c')
-rw-r--r-- | sound/oss/cs4232.c | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/sound/oss/cs4232.c b/sound/oss/cs4232.c new file mode 100644 index 000000000000..6ec308f5d935 --- /dev/null +++ b/sound/oss/cs4232.c | |||
@@ -0,0 +1,520 @@ | |||
1 | /* | ||
2 | * Copyright (C) by Hannu Savolainen 1993-1997 | ||
3 | * | ||
4 | * cs4232.c | ||
5 | * | ||
6 | * The low level driver for Crystal CS4232 based cards. The CS4232 is | ||
7 | * a PnP compatible chip which contains a CS4231A codec, SB emulation, | ||
8 | * a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM | ||
9 | * interfaces. This is just a temporary driver until full PnP support | ||
10 | * gets implemented. Just the WSS codec, FM synth and the MIDI ports are | ||
11 | * supported. Other interfaces are left uninitialized. | ||
12 | * | ||
13 | * ifdef ...WAVEFRONT... | ||
14 | * | ||
15 | * Support is provided for initializing the WaveFront synth | ||
16 | * interface as well, which is logical device #4. Note that if | ||
17 | * you have a Tropez+ card, you probably don't need to setup | ||
18 | * the CS4232-supported MIDI interface, since it corresponds to | ||
19 | * the internal 26-pin header that's hard to access. Using this | ||
20 | * requires an additional IRQ, a resource none too plentiful in | ||
21 | * this environment. Just don't set module parameters mpuio and | ||
22 | * mpuirq, and the MIDI port will be left uninitialized. You can | ||
23 | * still use the ICS2115 hosted MIDI interface which corresponds | ||
24 | * to the 9-pin D connector on the back of the card. | ||
25 | * | ||
26 | * endif ...WAVEFRONT... | ||
27 | * | ||
28 | * Supported chips are: | ||
29 | * CS4232 | ||
30 | * CS4236 | ||
31 | * CS4236B | ||
32 | * | ||
33 | * Note: You will need a PnP config setup to initialise some CS4232 boards | ||
34 | * anyway. | ||
35 | * | ||
36 | * Changes | ||
37 | * John Rood Added Bose Sound System Support. | ||
38 | * Toshio Spoor | ||
39 | * Alan Cox Modularisation, Basic cleanups. | ||
40 | * Paul Barton-Davis Separated MPU configuration, added | ||
41 | * Tropez+ (WaveFront) support | ||
42 | * Christoph Hellwig Adapted to module_init/module_exit, | ||
43 | * simple cleanups | ||
44 | * Arnaldo C. de Melo got rid of attach_uart401 | ||
45 | * Bartlomiej Zolnierkiewicz | ||
46 | * Added some __init/__initdata/__exit | ||
47 | * Marcus Meissner Added ISA PnP support. | ||
48 | */ | ||
49 | |||
50 | #include <linux/config.h> | ||
51 | #include <linux/pnp.h> | ||
52 | #include <linux/module.h> | ||
53 | #include <linux/init.h> | ||
54 | |||
55 | #include "sound_config.h" | ||
56 | |||
57 | #include "ad1848.h" | ||
58 | #include "mpu401.h" | ||
59 | |||
60 | #define KEY_PORT 0x279 /* Same as LPT1 status port */ | ||
61 | #define CSN_NUM 0x99 /* Just a random number */ | ||
62 | #define INDEX_ADDRESS 0x00 /* (R0) Index Address Register */ | ||
63 | #define INDEX_DATA 0x01 /* (R1) Indexed Data Register */ | ||
64 | #define PIN_CONTROL 0x0a /* (I10) Pin Control */ | ||
65 | #define ENABLE_PINS 0xc0 /* XCTRL0/XCTRL1 enable */ | ||
66 | |||
67 | static void CS_OUT(unsigned char a) | ||
68 | { | ||
69 | outb(a, KEY_PORT); | ||
70 | } | ||
71 | |||
72 | #define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);} | ||
73 | #define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} | ||
74 | |||
75 | static int __initdata bss = 0; | ||
76 | static int mpu_base, mpu_irq; | ||
77 | static int synth_base, synth_irq; | ||
78 | static int mpu_detected; | ||
79 | |||
80 | static int probe_cs4232_mpu(struct address_info *hw_config) | ||
81 | { | ||
82 | /* | ||
83 | * Just write down the config values. | ||
84 | */ | ||
85 | |||
86 | mpu_base = hw_config->io_base; | ||
87 | mpu_irq = hw_config->irq; | ||
88 | |||
89 | return 1; | ||
90 | } | ||
91 | |||
92 | static unsigned char crystal_key[] = /* A 32 byte magic key sequence */ | ||
93 | { | ||
94 | 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc, | ||
95 | 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2, | ||
96 | 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13, | ||
97 | 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a | ||
98 | }; | ||
99 | |||
100 | static void sleep(unsigned howlong) | ||
101 | { | ||
102 | current->state = TASK_INTERRUPTIBLE; | ||
103 | schedule_timeout(howlong); | ||
104 | } | ||
105 | |||
106 | static void enable_xctrl(int baseio) | ||
107 | { | ||
108 | unsigned char regd; | ||
109 | |||
110 | /* | ||
111 | * Some IBM Aptiva's have the Bose Sound System. By default | ||
112 | * the Bose Amplifier is disabled. The amplifier will be | ||
113 | * activated, by setting the XCTRL0 and XCTRL1 bits. | ||
114 | * Volume of the monitor bose speakers/woofer, can then | ||
115 | * be set by changing the PCM volume. | ||
116 | * | ||
117 | */ | ||
118 | |||
119 | printk("cs4232: enabling Bose Sound System Amplifier.\n"); | ||
120 | |||
121 | /* Switch to Pin Control Address */ | ||
122 | regd = inb(baseio + INDEX_ADDRESS) & 0xe0; | ||
123 | outb(((unsigned char) (PIN_CONTROL | regd)), baseio + INDEX_ADDRESS ); | ||
124 | |||
125 | /* Activate the XCTRL0 and XCTRL1 Pins */ | ||
126 | regd = inb(baseio + INDEX_DATA); | ||
127 | outb(((unsigned char) (ENABLE_PINS | regd)), baseio + INDEX_DATA ); | ||
128 | } | ||
129 | |||
130 | static int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) | ||
131 | { | ||
132 | int i, n; | ||
133 | int base = hw_config->io_base, irq = hw_config->irq; | ||
134 | int dma1 = hw_config->dma, dma2 = hw_config->dma2; | ||
135 | struct resource *ports; | ||
136 | |||
137 | if (base == -1 || irq == -1 || dma1 == -1) { | ||
138 | printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Verify that the I/O port range is free. | ||
144 | */ | ||
145 | |||
146 | ports = request_region(base, 4, "ad1848"); | ||
147 | if (!ports) { | ||
148 | printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); | ||
149 | return 0; | ||
150 | } | ||
151 | if (ad1848_detect(ports, NULL, hw_config->osp)) { | ||
152 | goto got_it; /* The card is already active */ | ||
153 | } | ||
154 | if (isapnp_configured) { | ||
155 | printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n"); | ||
156 | goto fail; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * This version of the driver doesn't use the PnP method when configuring | ||
161 | * the card but a simplified method defined by Crystal. This means that | ||
162 | * just one CS4232 compatible device can exist on the system. Also this | ||
163 | * method conflicts with possible PnP support in the OS. For this reason | ||
164 | * driver is just a temporary kludge. | ||
165 | * | ||
166 | * Also the Cirrus/Crystal method doesn't always work. Try ISA PnP first ;) | ||
167 | */ | ||
168 | |||
169 | /* | ||
170 | * Repeat initialization few times since it doesn't always succeed in | ||
171 | * first time. | ||
172 | */ | ||
173 | |||
174 | for (n = 0; n < 4; n++) | ||
175 | { | ||
176 | /* | ||
177 | * Wake up the card by sending a 32 byte Crystal key to the key port. | ||
178 | */ | ||
179 | |||
180 | for (i = 0; i < 32; i++) | ||
181 | CS_OUT(crystal_key[i]); | ||
182 | |||
183 | sleep(HZ / 10); | ||
184 | |||
185 | /* | ||
186 | * Now set the CSN (Card Select Number). | ||
187 | */ | ||
188 | |||
189 | CS_OUT2(0x06, CSN_NUM); | ||
190 | |||
191 | /* | ||
192 | * Then set some config bytes. First logical device 0 | ||
193 | */ | ||
194 | |||
195 | CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ | ||
196 | CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ | ||
197 | |||
198 | if (check_region(0x388, 4)) /* Not free */ | ||
199 | CS_OUT3(0x48, 0x00, 0x00) /* FM base off */ | ||
200 | else | ||
201 | CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */ | ||
202 | |||
203 | CS_OUT3(0x42, 0x00, 0x00); /* SB base off */ | ||
204 | CS_OUT2(0x22, irq); /* SB+WSS IRQ */ | ||
205 | CS_OUT2(0x2a, dma1); /* SB+WSS DMA */ | ||
206 | |||
207 | if (dma2 != -1) | ||
208 | CS_OUT2(0x25, dma2) /* WSS DMA2 */ | ||
209 | else | ||
210 | CS_OUT2(0x25, 4); /* No WSS DMA2 */ | ||
211 | |||
212 | CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */ | ||
213 | |||
214 | sleep(HZ / 10); | ||
215 | |||
216 | /* | ||
217 | * Initialize logical device 3 (MPU) | ||
218 | */ | ||
219 | |||
220 | if (mpu_base != 0 && mpu_irq != 0) | ||
221 | { | ||
222 | CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */ | ||
223 | CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ | ||
224 | CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ | ||
225 | CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ | ||
226 | } | ||
227 | |||
228 | if(synth_base != 0) | ||
229 | { | ||
230 | CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ | ||
231 | CS_OUT3 (0x47, (synth_base >> 8) & 0xff, | ||
232 | synth_base & 0xff); /* base */ | ||
233 | CS_OUT2 (0x22, synth_irq); /* IRQ */ | ||
234 | CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Finally activate the chip | ||
239 | */ | ||
240 | |||
241 | CS_OUT(0x79); | ||
242 | |||
243 | sleep(HZ / 5); | ||
244 | |||
245 | /* | ||
246 | * Then try to detect the codec part of the chip | ||
247 | */ | ||
248 | |||
249 | if (ad1848_detect(ports, NULL, hw_config->osp)) | ||
250 | goto got_it; | ||
251 | |||
252 | sleep(HZ); | ||
253 | } | ||
254 | fail: | ||
255 | release_region(base, 4); | ||
256 | return 0; | ||
257 | |||
258 | got_it: | ||
259 | if (dma2 == -1) | ||
260 | dma2 = dma1; | ||
261 | |||
262 | hw_config->slots[0] = ad1848_init("Crystal audio controller", ports, | ||
263 | irq, | ||
264 | dma1, /* Playback DMA */ | ||
265 | dma2, /* Capture DMA */ | ||
266 | 0, | ||
267 | hw_config->osp, | ||
268 | THIS_MODULE); | ||
269 | |||
270 | if (hw_config->slots[0] != -1 && | ||
271 | audio_devs[hw_config->slots[0]]->mixer_dev!=-1) | ||
272 | { | ||
273 | /* Assume the mixer map is as suggested in the CS4232 databook */ | ||
274 | AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); | ||
275 | AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); | ||
276 | AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */ | ||
277 | } | ||
278 | if (mpu_base != 0 && mpu_irq != 0) | ||
279 | { | ||
280 | static struct address_info hw_config2 = { | ||
281 | 0 | ||
282 | }; /* Ensure it's initialized */ | ||
283 | |||
284 | hw_config2.io_base = mpu_base; | ||
285 | hw_config2.irq = mpu_irq; | ||
286 | hw_config2.dma = -1; | ||
287 | hw_config2.dma2 = -1; | ||
288 | hw_config2.always_detect = 0; | ||
289 | hw_config2.name = NULL; | ||
290 | hw_config2.driver_use_1 = 0; | ||
291 | hw_config2.driver_use_2 = 0; | ||
292 | hw_config2.card_subtype = 0; | ||
293 | |||
294 | if (probe_uart401(&hw_config2, THIS_MODULE)) | ||
295 | { | ||
296 | mpu_detected = 1; | ||
297 | } | ||
298 | else | ||
299 | { | ||
300 | mpu_base = mpu_irq = 0; | ||
301 | } | ||
302 | hw_config->slots[1] = hw_config2.slots[1]; | ||
303 | } | ||
304 | |||
305 | if (bss) | ||
306 | enable_xctrl(base); | ||
307 | |||
308 | return 1; | ||
309 | } | ||
310 | |||
311 | static void __devexit unload_cs4232(struct address_info *hw_config) | ||
312 | { | ||
313 | int base = hw_config->io_base, irq = hw_config->irq; | ||
314 | int dma1 = hw_config->dma, dma2 = hw_config->dma2; | ||
315 | |||
316 | if (dma2 == -1) | ||
317 | dma2 = dma1; | ||
318 | |||
319 | ad1848_unload(base, | ||
320 | irq, | ||
321 | dma1, /* Playback DMA */ | ||
322 | dma2, /* Capture DMA */ | ||
323 | 0); | ||
324 | |||
325 | sound_unload_audiodev(hw_config->slots[0]); | ||
326 | if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) | ||
327 | { | ||
328 | static struct address_info hw_config2 = | ||
329 | { | ||
330 | 0 | ||
331 | }; /* Ensure it's initialized */ | ||
332 | |||
333 | hw_config2.io_base = mpu_base; | ||
334 | hw_config2.irq = mpu_irq; | ||
335 | hw_config2.dma = -1; | ||
336 | hw_config2.dma2 = -1; | ||
337 | hw_config2.always_detect = 0; | ||
338 | hw_config2.name = NULL; | ||
339 | hw_config2.driver_use_1 = 0; | ||
340 | hw_config2.driver_use_2 = 0; | ||
341 | hw_config2.card_subtype = 0; | ||
342 | hw_config2.slots[1] = hw_config->slots[1]; | ||
343 | |||
344 | unload_uart401(&hw_config2); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | static struct address_info cfg; | ||
349 | static struct address_info cfg_mpu; | ||
350 | |||
351 | static int __initdata io = -1; | ||
352 | static int __initdata irq = -1; | ||
353 | static int __initdata dma = -1; | ||
354 | static int __initdata dma2 = -1; | ||
355 | static int __initdata mpuio = -1; | ||
356 | static int __initdata mpuirq = -1; | ||
357 | static int __initdata synthio = -1; | ||
358 | static int __initdata synthirq = -1; | ||
359 | static int __initdata isapnp = 1; | ||
360 | |||
361 | MODULE_DESCRIPTION("CS4232 based soundcard driver"); | ||
362 | MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); | ||
363 | MODULE_LICENSE("GPL"); | ||
364 | |||
365 | module_param(io, int, 0); | ||
366 | MODULE_PARM_DESC(io,"base I/O port for AD1848"); | ||
367 | module_param(irq, int, 0); | ||
368 | MODULE_PARM_DESC(irq,"IRQ for AD1848 chip"); | ||
369 | module_param(dma, int, 0); | ||
370 | MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip"); | ||
371 | module_param(dma2, int, 0); | ||
372 | MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip"); | ||
373 | module_param(mpuio, int, 0); | ||
374 | MODULE_PARM_DESC(mpuio,"MPU 401 base address"); | ||
375 | module_param(mpuirq, int, 0); | ||
376 | MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ"); | ||
377 | module_param(synthio, int, 0); | ||
378 | MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port"); | ||
379 | module_param(synthirq, int, 0); | ||
380 | MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ"); | ||
381 | module_param(isapnp, bool, 0); | ||
382 | MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)"); | ||
383 | module_param(bss, bool, 0); | ||
384 | MODULE_PARM_DESC(bss,"Enable Bose Sound System Support (default 0)"); | ||
385 | |||
386 | /* | ||
387 | * Install a CS4232 based card. Need to have ad1848 and mpu401 | ||
388 | * loaded ready. | ||
389 | */ | ||
390 | |||
391 | /* All cs4232 based cards have the main ad1848 card either as CSC0000 or | ||
392 | * CSC0100. */ | ||
393 | static const struct pnp_device_id cs4232_pnp_table[] = { | ||
394 | { .id = "CSC0100", .driver_data = 0 }, | ||
395 | { .id = "CSC0000", .driver_data = 0 }, | ||
396 | /* Guillemot Turtlebeach something appears to be cs4232 compatible | ||
397 | * (untested) */ | ||
398 | { .id = "GIM0100", .driver_data = 0 }, | ||
399 | { .id = ""} | ||
400 | }; | ||
401 | |||
402 | MODULE_DEVICE_TABLE(pnp, cs4232_pnp_table); | ||
403 | |||
404 | static int cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) | ||
405 | { | ||
406 | struct address_info *isapnpcfg; | ||
407 | |||
408 | isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL); | ||
409 | if (!isapnpcfg) | ||
410 | return -ENOMEM; | ||
411 | |||
412 | isapnpcfg->irq = pnp_irq(dev, 0); | ||
413 | isapnpcfg->dma = pnp_dma(dev, 0); | ||
414 | isapnpcfg->dma2 = pnp_dma(dev, 1); | ||
415 | isapnpcfg->io_base = pnp_port_start(dev, 0); | ||
416 | if (probe_cs4232(isapnpcfg,TRUE) == 0) { | ||
417 | printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n"); | ||
418 | kfree(isapnpcfg); | ||
419 | return -ENODEV; | ||
420 | } | ||
421 | pnp_set_drvdata(dev,isapnpcfg); | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static void __devexit cs4232_pnp_remove(struct pnp_dev *dev) | ||
426 | { | ||
427 | struct address_info *cfg = pnp_get_drvdata(dev); | ||
428 | if (cfg) { | ||
429 | unload_cs4232(cfg); | ||
430 | kfree(cfg); | ||
431 | } | ||
432 | } | ||
433 | |||
434 | static struct pnp_driver cs4232_driver = { | ||
435 | .name = "cs4232", | ||
436 | .id_table = cs4232_pnp_table, | ||
437 | .probe = cs4232_pnp_probe, | ||
438 | .remove = __devexit_p(cs4232_pnp_remove), | ||
439 | }; | ||
440 | |||
441 | static int __init init_cs4232(void) | ||
442 | { | ||
443 | #ifdef CONFIG_SOUND_WAVEFRONT_MODULE | ||
444 | if(synthio == -1) | ||
445 | printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); | ||
446 | else { | ||
447 | synth_base = synthio; | ||
448 | synth_irq = synthirq; | ||
449 | } | ||
450 | #else | ||
451 | if(synthio != -1) | ||
452 | printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n"); | ||
453 | #endif | ||
454 | cfg.irq = -1; | ||
455 | |||
456 | if (isapnp && | ||
457 | (pnp_register_driver(&cs4232_driver) > 0) | ||
458 | ) | ||
459 | return 0; | ||
460 | |||
461 | if(io==-1||irq==-1||dma==-1) | ||
462 | { | ||
463 | printk(KERN_ERR "cs4232: Must set io, irq and dma.\n"); | ||
464 | return -ENODEV; | ||
465 | } | ||
466 | |||
467 | cfg.io_base = io; | ||
468 | cfg.irq = irq; | ||
469 | cfg.dma = dma; | ||
470 | cfg.dma2 = dma2; | ||
471 | |||
472 | cfg_mpu.io_base = -1; | ||
473 | cfg_mpu.irq = -1; | ||
474 | |||
475 | if (mpuio != -1 && mpuirq != -1) { | ||
476 | cfg_mpu.io_base = mpuio; | ||
477 | cfg_mpu.irq = mpuirq; | ||
478 | probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */ | ||
479 | } | ||
480 | |||
481 | if (probe_cs4232(&cfg,FALSE) == 0) | ||
482 | return -ENODEV; | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static void __exit cleanup_cs4232(void) | ||
488 | { | ||
489 | pnp_unregister_driver(&cs4232_driver); | ||
490 | if (cfg.irq != -1) | ||
491 | unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */ | ||
492 | } | ||
493 | |||
494 | module_init(init_cs4232); | ||
495 | module_exit(cleanup_cs4232); | ||
496 | |||
497 | #ifndef MODULE | ||
498 | static int __init setup_cs4232(char *str) | ||
499 | { | ||
500 | /* io, irq, dma, dma2 mpuio, mpuirq*/ | ||
501 | int ints[7]; | ||
502 | |||
503 | /* If we have isapnp cards, no need for options */ | ||
504 | if (pnp_register_driver(&cs4232_driver) > 0) | ||
505 | return 1; | ||
506 | |||
507 | str = get_options(str, ARRAY_SIZE(ints), ints); | ||
508 | |||
509 | io = ints[1]; | ||
510 | irq = ints[2]; | ||
511 | dma = ints[3]; | ||
512 | dma2 = ints[4]; | ||
513 | mpuio = ints[5]; | ||
514 | mpuirq = ints[6]; | ||
515 | |||
516 | return 1; | ||
517 | } | ||
518 | |||
519 | __setup("cs4232=", setup_cs4232); | ||
520 | #endif | ||