1/*
2   sound/oss/aedsp16.c
3
4   Audio Excel DSP 16 software configuration routines
5   Copyright (C) 1995,1996,1997,1998  Riccardo Facchetti (fizban@tin.it)
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22/*
23 * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
24 * headers needed by this source.
25 */
26#include <linux/delay.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include "sound_config.h"
30
31/*
32
33   READ THIS
34
35   This module started to configure the Audio Excel DSP 16 Sound Card.
36   Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
37
38   NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
39   audio card and want to see the kernel support for it, please contact me.
40
41   Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
42   compatible card.
43   It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
44   so before this module, the only way to configure the DSP under linux was
45   boot the MS-DOS loading the sound.sys device driver (this driver soft-
46   configure the sound board hardware by massaging someone of its registers),
47   and then ctrl-alt-del to boot linux with the DSP configured by the DOS
48   driver.
49
50   This module works configuring your Audio Excel DSP 16's irq, dma and
51   mpu-401-irq. The OSS Lite routines rely on the fact that if the
52   hardware is there, they can detect it. The problem with AEDSP16 is
53   that no hardware can be found by the probe routines if the sound card
54   is not configured properly. Sometimes the kernel probe routines can find
55   an SBPRO even when the card is not configured (this is the standard setup
56   of the card), but the SBPRO emulation don't work well if the card is not
57   properly initialized. For this reason
58
59   aedsp16_init_board()
60
61   routine is called before the OSS Lite probe routines try to detect the
62   hardware.
63
64   NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
65
66   NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
67   have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
68   have to be configured by software.
69
70   NOTE: The driver is merged with the new OSS Lite sound driver. It works
71   as a lowlevel driver.
72
73   The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
74   the OSS Lite sound driver can be configured for SBPRO and MSS cards
75   at the same time, but the aedsp16 can't be two cards!!
76   When we configure it, we have to choose the SBPRO or the MSS emulation
77   for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
78
79   NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
80   please let me know if it works.
81
82   The MPU-401 support can be compiled in together with one of the other
83   two operating modes.
84
85   NOTE: This is something like plug-and-play: we have only to plug
86   the AEDSP16 board in the socket, and then configure and compile
87   a kernel that uses the AEDSP16 software configuration capability.
88   No jumper setting is needed!
89
90   For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
91   you have just to make config the OSS Lite package, configuring
92   the AEDSP16 sound card, then activating the SBPro emulation mode
93   and at last configuring IRQ and DMA.
94   Compile the kernel and run it.
95
96   NOTE: This means for SC-6000 cards that you can choose irq and dma,
97   but not the I/O addresses. To change I/O addresses you have to set
98   them with jumpers. For SC-6600 cards you have no jumpers so you have
99   to set up your full card configuration in the make config.
100
101   You can change the irq/dma/mirq settings WITHOUT THE NEED to open
102   your computer and massage the jumpers (there are no irq/dma/mirq
103   jumpers to be configured anyway, only I/O BASE values have to be
104   configured with jumpers)
105
106   For some ununderstandable reason, the card default of irq 7, dma 1,
107   don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
108   HDD work, the kernel start to erupt out a lot of messages like:
109
110   'Sound: DMA timed out - IRQ/DRQ config error?'
111
112   For what I can say, I have NOT any conflict at irq 7 (under linux I'm
113   using the lp polling driver), and dma line 1 is unused as stated by
114   /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
115   I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
116   Anyway a setting of irq 10, dma 3 works really fine.
117
118   NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
119   the emulation mode, all the installed hardware and the hardware
120   configuration (irq and dma settings of all the hardware).
121
122   This init module should work with SBPRO+MSS, when one of the two is
123   the AEDSP16 emulation and the other the real card. (see [1])
124   For example:
125
126   AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
127   AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
128
129   MPU401 should work. (see [2])
130
131   [1]
132       ---
133       Date: Mon, 29 Jul 1997 08:35:40 +0100
134       From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
135
136       [...]
137       Just to let you know got my Audio Excel (emulating a MSS) working
138       with my original SB16, thanks for the driver!
139       [...]
140       ---
141
142   [2] Not tested by me for lack of hardware.
143
144   TODO, WISHES AND TECH
145
146   - About I/O ports allocation -
147
148   Request the 2x0h region (port base) in any case if we are using this card.
149
150   NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
151   port base region (see code) does not mean necessarily that we are emulating
152   sbpro.  Even if this region is the sbpro I/O ports region, we use this
153   region to access the control registers of the card, and if emulating
154   sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
155   registers are not used, in no way, to emulate an sbpro: they are
156   used only for configuration purposes.
157
158   Started Fri Mar 17 16:13:18 MET 1995
159
160   v0.1 (ALPHA, was a user-level program called AudioExcelDSP16.c)
161   - Initial code.
162   v0.2 (ALPHA)
163   - Cleanups.
164   - Integrated with Linux voxware v 2.90-2 kernel sound driver.
165   - SoundBlaster Pro mode configuration.
166   - Microsoft Sound System mode configuration.
167   - MPU-401 mode configuration.
168   v0.3 (ALPHA)
169   - Cleanups.
170   - Rearranged the code to let aedsp16_init_board be more general.
171   - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
172   inclusion too. We rely on os.h
173   - Used the  to get a variable
174   len string (we are not sure about the len of Copyright string).
175   This works with any SB and compatible.
176   - Added the code to request_region at device init (should go in
177   the main body of voxware).
178   v0.4 (BETA)
179   - Better configure.c patch for aedsp16 configuration (better
180   logic of inclusion of AEDSP16 support)
181   - Modified the conditional compilation to better support more than
182   one sound card of the emulated type (read the NOTES above)
183   - Moved the sb init routine from the attach to the very first
184   probe in sb_card.c
185   - Rearrangements and cleanups
186   - Wiped out some unnecessary code and variables: this is kernel
187   code so it is better save some TEXT and DATA
188   - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
189   I/O ports in any case because they are used to access the DSP
190   configuration registers and we can not allow anyone to get them.
191   v0.5
192   - cleanups on comments
193   - prep for diffs against v3.0-proto-950402
194   v0.6
195   - removed the request_region()s when compiling the MODULE sound.o
196   because we are not allowed (by the actual voxware structure) to
197   release_region()
198   v0.7 (pre ALPHA, not distributed)
199   - started porting this module to kernel 1.3.84. Dummy probe/attach
200   routines.
201   v0.8 (ALPHA)
202   - attached all the init routines.
203   v0.9 (BETA)
204   - Integrated with linux-pre2.0.7
205   - Integrated with configuration scripts.
206   - Cleaned up and beautyfied the code.
207   v0.9.9 (BETA)
208   - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
209     Now only the code configured is compiled in, with some memory saving.
210   v0.9.10
211   - Integration into the sound/lowlevel/ section of the sound driver.
212   - Re-organized the code.
213   v0.9.11 (not distributed)
214   - Rewritten the init interface-routines to initialize the AEDSP16 in
215     one shot.
216   - More cosmetics.
217   - SC-6600 support.
218   - More soft/hard configuration.
219   v0.9.12
220   - Refined the v0.9.11 code with conditional compilation to distinguish
221     between SC-6000 and SC-6600 code.
222   v1.0.0
223   - Prep for merging with OSS Lite and Linux kernel 2.1.13
224   - Corrected a bug in request/check/release region calls (thanks to the
225     new kernel exception handling).
226   v1.1
227   - Revamped for integration with new modularized sound drivers: to enhance
228     the flexibility of modular version, I have removed all the conditional
229     compilation for SBPRO, MPU and MSS code. Now it is all managed with
230     the ae_config structure.
231   v1.2
232   - Module informations added.
233   - Removed aedsp16_delay_10msec(), now using mdelay(10)
234   - All data and funcs moved to .*.init section.
235   v1.3
236   Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/27
237   - got rid of check_region
238
239   Known Problems:
240   - Audio Excel DSP 16 III don't work with this driver.
241
242   Credits:
243   Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
244   lot in testing the 0.9.11 and 0.9.12 versions of this driver.
245
246 */
247
248
249#define VERSION "1.3"		/* Version of Audio Excel DSP 16 driver */
250
251#undef	AEDSP16_DEBUG 		/* Define this to 1 to enable debug code     */
252#undef	AEDSP16_DEBUG_MORE 	/* Define this to 1 to enable more debug     */
253#undef	AEDSP16_INFO 		/* Define this to 1 to enable info code      */
254
255#if defined(AEDSP16_DEBUG)
256# define DBG(x)  printk x
257# if defined(AEDSP16_DEBUG_MORE)
258#  define DBG1(x) printk x
259# else
260#  define DBG1(x)
261# endif
262#else
263# define DBG(x)
264# define DBG1(x)
265#endif
266
267/*
268 * Misc definitions
269 */
270#define TRUE	1
271#define FALSE	0
272
273/*
274 * Region Size for request/check/release region.
275 */
276#define IOBASE_REGION_SIZE	0x10
277
278/*
279 * Hardware related defaults
280 */
281#define DEF_AEDSP16_IOB 0x220   /* 0x220(default) 0x240                 */
282#define DEF_AEDSP16_IRQ 7	/* 5 7(default) 9 10 11                 */
283#define DEF_AEDSP16_MRQ 0	/* 5 7 9 10 0(default), 0 means disable */
284#define DEF_AEDSP16_DMA 1	/* 0 1(default) 3                       */
285
286/*
287 * Commands of AEDSP16's DSP (SBPRO+special).
288 * Some of them are COMMAND_xx, in the future they may change.
289 */
290#define WRITE_MDIRQ_CFG   0x50	/* Set M&I&DRQ mask (the real config)   */
291#define COMMAND_52        0x52	/*                                      */
292#define READ_HARD_CFG     0x58	/* Read Hardware Config (I/O base etc)  */
293#define COMMAND_5C        0x5c	/*                                      */
294#define COMMAND_60        0x60	/*                                      */
295#define COMMAND_66        0x66	/*                                      */
296#define COMMAND_6C        0x6c	/*                                      */
297#define COMMAND_6E        0x6e	/*                                      */
298#define COMMAND_88        0x88	/*                                      */
299#define DSP_INIT_MSS      0x8c	/* Enable Microsoft Sound System mode   */
300#define COMMAND_C5        0xc5	/*                                      */
301#define GET_DSP_VERSION   0xe1	/* Get DSP Version                      */
302#define GET_DSP_COPYRIGHT 0xe3	/* Get DSP Copyright                    */
303
304/*
305 * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
306 * to have the actual I/O port.
307 * Register permissions are:
308 * (wo) == Write Only
309 * (ro) == Read  Only
310 * (w-) == Write
311 * (r-) == Read
312 */
313#define DSP_RESET    0x06	/* offset of DSP RESET             (wo) */
314#define DSP_READ     0x0a	/* offset of DSP READ              (ro) */
315#define DSP_WRITE    0x0c	/* offset of DSP WRITE             (w-) */
316#define DSP_COMMAND  0x0c	/* offset of DSP COMMAND           (w-) */
317#define DSP_STATUS   0x0c	/* offset of DSP STATUS            (r-) */
318#define DSP_DATAVAIL 0x0e	/* offset of DSP DATA AVAILABLE    (ro) */
319
320
321#define RETRY           10	/* Various retry values on I/O opera-   */
322#define STATUSRETRY   1000	/* tions. Sometimes we have to          */
323#define HARDRETRY   500000	/* wait for previous cmd to complete    */
324
325/*
326 * Size of character arrays that store name and version of sound card
327 */
328#define CARDNAMELEN	15	/* Size of the card's name in chars     */
329#define CARDVERLEN	10	/* Size of the card's version in chars	*/
330#define CARDVERDIGITS	2	/* Number of digits in the version	*/
331
332#if defined(CONFIG_SC6600)
333/*
334 * Bitmapped flags of hard configuration
335 */
336/*
337 * Decode macros (xl == low byte, xh = high byte)
338 */
339#define IOBASE(xl)		((xl & 0x01)?0x240:0x220)
340#define JOY(xl)  		(xl & 0x02)
341#define MPUADDR(xl)		( 			\
342				(xl & 0x0C)?0x330:	\
343				(xl & 0x08)?0x320:	\
344				(xl & 0x04)?0x310:	\
345						0x300)
346#define WSSADDR(xl)		((xl & 0x10)?0xE80:0x530)
347#define CDROM(xh)		(xh & 0x20)
348#define CDROMADDR(xh)		(((xh & 0x1F) << 4) + 0x200)
349/*
350 * Encode macros
351 */
352#define BLDIOBASE(xl, val) {		\
353	xl &= ~0x01; 			\
354	if (val == 0x240)		\
355		xl |= 0x01;		\
356	}
357#define BLDJOY(xl, val) {		\
358	xl &= ~0x02; 			\
359	if (val == 1)			\
360		xl |= 0x02;		\
361	}
362#define BLDMPUADDR(xl, val) {		\
363	xl &= ~0x0C;			\
364	switch (val) {			\
365		case 0x330:		\
366			xl |= 0x0C;	\
367			break;		\
368		case 0x320:		\
369			xl |= 0x08;	\
370			break;		\
371		case 0x310:		\
372			xl |= 0x04;	\
373			break;		\
374		case 0x300:		\
375			xl |= 0x00;	\
376			break;		\
377		default:		\
378			xl |= 0x00;	\
379			break;		\
380		}			\
381	}
382#define BLDWSSADDR(xl, val) {		\
383	xl &= ~0x10; 			\
384	if (val == 0xE80)		\
385		xl |= 0x10;		\
386	}
387#define BLDCDROM(xh, val) {		\
388	xh &= ~0x20; 			\
389	if (val == 1)			\
390		xh |= 0x20;		\
391	}
392#define BLDCDROMADDR(xh, val) {		\
393	int tmp = val;			\
394	tmp -= 0x200;			\
395	tmp >>= 4;			\
396	tmp &= 0x1F;			\
397	xh |= tmp;			\
398	xh &= 0x7F;			\
399	xh |= 0x40;			\
400	}
401#endif /* CONFIG_SC6600 */
402
403/*
404 * Bit mapped flags for calling aedsp16_init_board(), and saving the current
405 * emulation mode.
406 */
407#define INIT_NONE   (0   )
408#define INIT_SBPRO  (1<<0)
409#define INIT_MSS    (1<<1)
410#define INIT_MPU401 (1<<2)
411
412static int      soft_cfg __initdata = 0;	/* bitmapped config */
413static int      soft_cfg_mss __initdata = 0;	/* bitmapped mss config */
414static int      ver[CARDVERDIGITS] __initdata = {0, 0};	/* DSP Ver:
415						   hi->ver[0] lo->ver[1] */
416
417#if defined(CONFIG_SC6600)
418static int	hard_cfg[2]     /* lo<-hard_cfg[0] hi<-hard_cfg[1]      */
419                     __initdata = { 0, 0};
420#endif /* CONFIG_SC6600 */
421
422#if defined(CONFIG_SC6600)
423/* Decoded hard configuration */
424struct	d_hcfg {
425	int iobase;
426	int joystick;
427	int mpubase;
428	int wssbase;
429	int cdrom;
430	int cdrombase;
431};
432
433static struct d_hcfg decoded_hcfg __initdata = {0, };
434
435#endif /* CONFIG_SC6600 */
436
437/* orVals contain the values to be or'ed       				*/
438struct orVals {
439	int	val;		/* irq|mirq|dma                         */
440	int	or;		/* soft_cfg |= TheStruct.or             */
441};
442
443/* aedsp16_info contain the audio card configuration                  */
444struct aedsp16_info {
445	int base_io;            /* base I/O address for accessing card  */
446	int irq;                /* irq value for DSP I/O                */
447	int mpu_irq;            /* irq for mpu401 interface I/O         */
448	int dma;                /* dma value for DSP I/O                */
449	int mss_base;           /* base I/O for Microsoft Sound System  */
450	int mpu_base;           /* base I/O for MPU-401 emulation       */
451	int init;               /* Initialization status of the card    */
452};
453
454/*
455 * Magic values that the DSP will eat when configuring irq/mirq/dma
456 */
457/* DSP IRQ conversion array             */
458static struct orVals orIRQ[] __initdata = {
459	{0x05, 0x28},
460	{0x07, 0x08},
461	{0x09, 0x10},
462	{0x0a, 0x18},
463	{0x0b, 0x20},
464	{0x00, 0x00}
465};
466
467/* MPU-401 IRQ conversion array         */
468static struct orVals orMIRQ[] __initdata = {
469	{0x05, 0x04},
470	{0x07, 0x44},
471	{0x09, 0x84},
472	{0x0a, 0xc4},
473	{0x00, 0x00}
474};
475
476/* DMA Channels conversion array        */
477static struct orVals orDMA[] __initdata = {
478	{0x00, 0x01},
479	{0x01, 0x02},
480	{0x03, 0x03},
481	{0x00, 0x00}
482};
483
484static struct aedsp16_info ae_config = {
485	DEF_AEDSP16_IOB,
486	DEF_AEDSP16_IRQ,
487	DEF_AEDSP16_MRQ,
488	DEF_AEDSP16_DMA,
489	-1,
490	-1,
491	INIT_NONE
492};
493
494/*
495 * Buffers to store audio card informations
496 */
497static char     DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
498static char     DSPVersion[CARDVERLEN + 1] __initdata = {0, };
499
500static int __init aedsp16_wait_data(int port)
501{
502	int             loop = STATUSRETRY;
503	unsigned char   ret = 0;
504
505	DBG1(("aedsp16_wait_data (0x%x): ", port));
506
507	do {
508		  ret = inb(port + DSP_DATAVAIL);
509	/*
510	 * Wait for data available (bit 7 of ret == 1)
511	 */
512	  } while (!(ret & 0x80) && loop--);
513
514	if (ret & 0x80) {
515		DBG1(("success.\n"));
516		return TRUE;
517	}
518
519	DBG1(("failure.\n"));
520	return FALSE;
521}
522
523static int __init aedsp16_read(int port)
524{
525	int inbyte;
526
527	DBG(("    Read DSP Byte (0x%x): ", port));
528
529	if (aedsp16_wait_data(port) == FALSE) {
530		DBG(("failure.\n"));
531		return -1;
532	}
533
534	inbyte = inb(port + DSP_READ);
535
536	DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
537
538	return inbyte;
539}
540
541static int __init aedsp16_test_dsp(int port)
542{
543	return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
544}
545
546static int __init aedsp16_dsp_reset(int port)
547{
548	/*
549	 * Reset DSP
550	 */
551
552	DBG(("Reset DSP:\n"));
553
554	outb(1, (port + DSP_RESET));
555	udelay(10);
556	outb(0, (port + DSP_RESET));
557	udelay(10);
558	udelay(10);
559	if (aedsp16_test_dsp(port) == TRUE) {
560		DBG(("success.\n"));
561		return TRUE;
562	} else
563		DBG(("failure.\n"));
564	return FALSE;
565}
566
567static int __init aedsp16_write(int port, int cmd)
568{
569	unsigned char   ret;
570	int             loop = HARDRETRY;
571
572	DBG(("    Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
573
574	do {
575		ret = inb(port + DSP_STATUS);
576		/*
577		 * DSP ready to receive data if bit 7 of ret == 0
578		 */
579		if (!(ret & 0x80)) {
580			outb(cmd, port + DSP_COMMAND);
581			DBG(("success.\n"));
582			return 0;
583		}
584	} while (loop--);
585
586	DBG(("timeout.\n"));
587	printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
588
589	return -1;
590}
591
592#if defined(CONFIG_SC6600)
593
594#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
595void __init aedsp16_pinfo(void) {
596	DBG(("\n Base address:  %x\n", decoded_hcfg.iobase));
597	DBG((" Joystick    : %s present\n", decoded_hcfg.joystick?"":" not"));
598	DBG((" WSS addr    :  %x\n", decoded_hcfg.wssbase));
599	DBG((" MPU-401 addr:  %x\n", decoded_hcfg.mpubase));
600	DBG((" CDROM       : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
601	DBG((" CDROMADDR   :  %x\n\n", decoded_hcfg.cdrombase));
602}
603#endif
604
605static void __init aedsp16_hard_decode(void) {
606
607	DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
608
609/*
610 * Decode Cfg Bytes.
611 */
612	decoded_hcfg.iobase	= IOBASE(hard_cfg[0]);
613	decoded_hcfg.joystick	= JOY(hard_cfg[0]);
614	decoded_hcfg.wssbase	= WSSADDR(hard_cfg[0]);
615	decoded_hcfg.mpubase	= MPUADDR(hard_cfg[0]);
616	decoded_hcfg.cdrom	= CDROM(hard_cfg[1]);
617	decoded_hcfg.cdrombase	= CDROMADDR(hard_cfg[1]);
618
619#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
620	printk(" Original sound card configuration:\n");
621	aedsp16_pinfo();
622#endif
623
624/*
625 * Now set up the real kernel configuration.
626 */
627	decoded_hcfg.iobase	= ae_config.base_io;
628	decoded_hcfg.wssbase	= ae_config.mss_base;
629	decoded_hcfg.mpubase	= ae_config.mpu_base;
630
631#if defined(CONFIG_SC6600_JOY)
632 	decoded_hcfg.joystick	= CONFIG_SC6600_JOY; /* Enable */
633#endif
634#if defined(CONFIG_SC6600_CDROM)
635	decoded_hcfg.cdrom	= CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
636#endif
637#if defined(CONFIG_SC6600_CDROMBASE)
638	decoded_hcfg.cdrombase	= CONFIG_SC6600_CDROMBASE; /* 0 Disable */
639#endif
640
641#if defined(AEDSP16_DEBUG)
642	DBG((" New Values:\n"));
643	aedsp16_pinfo();
644#endif
645
646	DBG(("success.\n"));
647}
648
649static void __init aedsp16_hard_encode(void) {
650
651	DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
652
653	hard_cfg[0] = 0;
654	hard_cfg[1] = 0;
655
656	hard_cfg[0] |= 0x20;
657
658	BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
659	BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
660	BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
661	BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
662	BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
663	BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
664
665#if defined(AEDSP16_DEBUG)
666	aedsp16_pinfo();
667#endif
668
669	DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
670	DBG(("success.\n"));
671
672}
673
674static int __init aedsp16_hard_write(int port) {
675
676	DBG(("aedsp16_hard_write:\n"));
677
678	if (aedsp16_write(port, COMMAND_6C)) {
679		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
680		DBG(("failure.\n"));
681		return FALSE;
682	}
683	if (aedsp16_write(port, COMMAND_5C)) {
684		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
685		DBG(("failure.\n"));
686		return FALSE;
687	}
688	if (aedsp16_write(port, hard_cfg[0])) {
689		printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
690		DBG(("failure.\n"));
691		return FALSE;
692	}
693	if (aedsp16_write(port, hard_cfg[1])) {
694		printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
695		DBG(("failure.\n"));
696		return FALSE;
697	}
698	if (aedsp16_write(port, COMMAND_C5)) {
699		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
700		DBG(("failure.\n"));
701		return FALSE;
702	}
703
704	DBG(("success.\n"));
705
706	return TRUE;
707}
708
709static int __init aedsp16_hard_read(int port) {
710
711	DBG(("aedsp16_hard_read:\n"));
712
713	if (aedsp16_write(port, READ_HARD_CFG)) {
714		printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
715		DBG(("failure.\n"));
716		return FALSE;
717	}
718
719	if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
720		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
721			READ_HARD_CFG);
722		DBG(("failure.\n"));
723		return FALSE;
724	}
725	if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
726		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
727			READ_HARD_CFG);
728		DBG(("failure.\n"));
729		return FALSE;
730	}
731	if (aedsp16_read(port) == -1) {
732		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
733			READ_HARD_CFG);
734		DBG(("failure.\n"));
735		return FALSE;
736	}
737
738	DBG(("success.\n"));
739
740	return TRUE;
741}
742
743static int __init aedsp16_ext_cfg_write(int port) {
744
745	int extcfg, val;
746
747	if (aedsp16_write(port, COMMAND_66)) {
748		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
749		return FALSE;
750	}
751
752	extcfg = 7;
753	if (decoded_hcfg.cdrom != 2)
754		extcfg = 0x0F;
755	if ((decoded_hcfg.cdrom == 4) ||
756	    (decoded_hcfg.cdrom == 3))
757		extcfg &= ~2;
758	if (decoded_hcfg.cdrombase == 0)
759		extcfg &= ~2;
760	if (decoded_hcfg.mpubase == 0)
761		extcfg &= ~1;
762
763	if (aedsp16_write(port, extcfg)) {
764		printk("[AEDSP16] Write extcfg: failed!\n");
765		return FALSE;
766	}
767	if (aedsp16_write(port, 0)) {
768		printk("[AEDSP16] Write extcfg: failed!\n");
769		return FALSE;
770	}
771	if (decoded_hcfg.cdrom == 3) {
772		if (aedsp16_write(port, COMMAND_52)) {
773			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
774			return FALSE;
775		}
776		if ((val = aedsp16_read(port)) == -1) {
777			printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
778					, COMMAND_52);
779			return FALSE;
780		}
781		val &= 0x7F;
782		if (aedsp16_write(port, COMMAND_60)) {
783			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
784			return FALSE;
785		}
786		if (aedsp16_write(port, val)) {
787			printk("[AEDSP16] Write val: failed!\n");
788			return FALSE;
789		}
790	}
791
792	return TRUE;
793}
794
795#endif /* CONFIG_SC6600 */
796
797static int __init aedsp16_cfg_write(int port) {
798	if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
799		printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
800		return FALSE;
801	}
802	if (aedsp16_write(port, soft_cfg)) {
803		printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
804		return FALSE;
805	}
806	return TRUE;
807}
808
809static int __init aedsp16_init_mss(int port)
810{
811	DBG(("aedsp16_init_mss:\n"));
812
813	mdelay(10);
814
815	if (aedsp16_write(port, DSP_INIT_MSS)) {
816		printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
817				DSP_INIT_MSS);
818		DBG(("failure.\n"));
819		return FALSE;
820	}
821
822	mdelay(10);
823
824	if (aedsp16_cfg_write(port) == FALSE)
825		return FALSE;
826
827	outb(soft_cfg_mss, ae_config.mss_base);
828
829	DBG(("success.\n"));
830
831	return TRUE;
832}
833
834static int __init aedsp16_setup_board(int port) {
835	int	loop = RETRY;
836
837#if defined(CONFIG_SC6600)
838	int	val = 0;
839
840	if (aedsp16_hard_read(port) == FALSE) {
841		printk("[AEDSP16] aedsp16_hard_read: failed!\n");
842		return FALSE;
843	}
844
845	if (aedsp16_write(port, COMMAND_52)) {
846		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
847		return FALSE;
848	}
849
850	if ((val = aedsp16_read(port)) == -1) {
851		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
852				COMMAND_52);
853		return FALSE;
854	}
855#endif
856
857	do {
858		if (aedsp16_write(port, COMMAND_88)) {
859			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
860			return FALSE;
861		}
862		mdelay(10);
863	} while ((aedsp16_wait_data(port) == FALSE) && loop--);
864
865	if (aedsp16_read(port) == -1) {
866		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
867				COMMAND_88);
868		return FALSE;
869	}
870
871#if !defined(CONFIG_SC6600)
872	if (aedsp16_write(port, COMMAND_5C)) {
873		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
874		return FALSE;
875	}
876#endif
877
878	if (aedsp16_cfg_write(port) == FALSE)
879		return FALSE;
880
881#if defined(CONFIG_SC6600)
882	if (aedsp16_write(port, COMMAND_60)) {
883		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
884		return FALSE;
885	}
886	if (aedsp16_write(port, val)) {
887		printk("[AEDSP16] DATA 0x%x: failed!\n", val);
888		return FALSE;
889	}
890	if (aedsp16_write(port, COMMAND_6E)) {
891		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
892		return FALSE;
893	}
894	if (aedsp16_write(port, ver[0])) {
895		printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
896		return FALSE;
897	}
898	if (aedsp16_write(port, ver[1])) {
899		printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
900		return FALSE;
901	}
902
903	if (aedsp16_hard_write(port) == FALSE) {
904		printk("[AEDSP16] aedsp16_hard_write: failed!\n");
905		return FALSE;
906	}
907
908	if (aedsp16_write(port, COMMAND_5C)) {
909		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
910		return FALSE;
911	}
912
913#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
914	if (aedsp16_cfg_write(port) == FALSE)
915		return FALSE;
916#endif
917
918#endif
919
920	return TRUE;
921}
922
923static int __init aedsp16_stdcfg(int port) {
924	if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
925		printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
926		return FALSE;
927	}
928	/*
929	 * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
930	 */
931	if (aedsp16_write(port, 0x0A)) {
932		printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
933		return FALSE;
934	}
935	return TRUE;
936}
937
938static int __init aedsp16_dsp_version(int port)
939{
940	int             len = 0;
941	int             ret;
942
943	DBG(("Get DSP Version:\n"));
944
945	if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
946		printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
947		DBG(("failed.\n"));
948		return FALSE;
949	}
950
951	do {
952		if ((ret = aedsp16_read(port)) == -1) {
953			DBG(("failed.\n"));
954			return FALSE;
955		}
956	/*
957	 * We already know how many int are stored (2), so we know when the
958	 * string is finished.
959	 */
960		ver[len++] = ret;
961	  } while (len < CARDVERDIGITS);
962	sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
963
964	DBG(("success.\n"));
965
966	return TRUE;
967}
968
969static int __init aedsp16_dsp_copyright(int port)
970{
971	int             len = 0;
972	int             ret;
973
974	DBG(("Get DSP Copyright:\n"));
975
976	if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
977		printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
978		DBG(("failed.\n"));
979		return FALSE;
980	}
981
982	do {
983		if ((ret = aedsp16_read(port)) == -1) {
984	/*
985	 * If no more data available, return to the caller, no error if len>0.
986	 * We have no other way to know when the string is finished.
987	 */
988			if (len)
989				break;
990			else {
991				DBG(("failed.\n"));
992				return FALSE;
993			}
994		}
995
996		DSPCopyright[len++] = ret;
997
998	  } while (len < CARDNAMELEN);
999
1000	DBG(("success.\n"));
1001
1002	return TRUE;
1003}
1004
1005static void __init aedsp16_init_tables(void)
1006{
1007	int i = 0;
1008
1009	memset(DSPCopyright, 0, CARDNAMELEN + 1);
1010	memset(DSPVersion, 0, CARDVERLEN + 1);
1011
1012	for (i = 0; orIRQ[i].or; i++)
1013		if (orIRQ[i].val == ae_config.irq) {
1014			soft_cfg |= orIRQ[i].or;
1015			soft_cfg_mss |= orIRQ[i].or;
1016		}
1017
1018	for (i = 0; orMIRQ[i].or; i++)
1019		if (orMIRQ[i].or == ae_config.mpu_irq)
1020			soft_cfg |= orMIRQ[i].or;
1021
1022	for (i = 0; orDMA[i].or; i++)
1023		if (orDMA[i].val == ae_config.dma) {
1024			soft_cfg |= orDMA[i].or;
1025			soft_cfg_mss |= orDMA[i].or;
1026		}
1027}
1028
1029static int __init aedsp16_init_board(void)
1030{
1031	aedsp16_init_tables();
1032
1033	if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
1034		printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
1035		return FALSE;
1036	}
1037	if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
1038		printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
1039		return FALSE;
1040	}
1041
1042	/*
1043	 * My AEDSP16 card return SC-6000 in DSPCopyright, so
1044	 * if we have something different, we have to be warned.
1045	 */
1046	if (strcmp("SC-6000", DSPCopyright))
1047		printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
1048
1049	if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
1050		printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
1051		return FALSE;
1052	}
1053
1054	if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
1055		printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
1056		return FALSE;
1057	}
1058
1059#if defined(CONFIG_SC6600)
1060	if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
1061		printk("[AEDSP16] aedsp16_hard_read: failed!\n");
1062		return FALSE;
1063	}
1064
1065	aedsp16_hard_decode();
1066
1067	aedsp16_hard_encode();
1068
1069	if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
1070		printk("[AEDSP16] aedsp16_hard_write: failed!\n");
1071		return FALSE;
1072	}
1073
1074	if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
1075		printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
1076		return FALSE;
1077	}
1078#endif /* CONFIG_SC6600 */
1079
1080	if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
1081		printk("[AEDSP16] aedsp16_setup_board: failed!\n");
1082		return FALSE;
1083	}
1084
1085	if (ae_config.mss_base != -1) {
1086		if (ae_config.init & INIT_MSS) {
1087			if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
1088				printk("[AEDSP16] Can not initialize"
1089				       "Microsoft Sound System mode.\n");
1090				return FALSE;
1091			}
1092		}
1093	}
1094
1095#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
1096
1097	printk("Audio Excel DSP 16 init v%s (%s %s) [",
1098		VERSION, DSPCopyright,
1099		DSPVersion);
1100
1101	if (ae_config.mpu_base != -1) {
1102		if (ae_config.init & INIT_MPU401) {
1103			printk("MPU401");
1104			if ((ae_config.init & INIT_MSS) ||
1105			    (ae_config.init & INIT_SBPRO))
1106				printk(" ");
1107		}
1108	}
1109
1110	if (ae_config.mss_base == -1) {
1111		if (ae_config.init & INIT_SBPRO) {
1112			printk("SBPro");
1113			if (ae_config.init & INIT_MSS)
1114				printk(" ");
1115		}
1116	}
1117
1118	if (ae_config.mss_base != -1)
1119		if (ae_config.init & INIT_MSS)
1120			printk("MSS");
1121
1122	printk("]\n");
1123#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
1124
1125	mdelay(10);
1126
1127	return TRUE;
1128}
1129
1130static int __init init_aedsp16_sb(void)
1131{
1132	DBG(("init_aedsp16_sb: "));
1133
1134/*
1135 * If the card is already init'ed MSS, we can not init it to SBPRO too
1136 * because the board can not emulate simultaneously MSS and SBPRO.
1137 */
1138	if (ae_config.init & INIT_MSS)
1139		return FALSE;
1140	if (ae_config.init & INIT_SBPRO)
1141		return FALSE;
1142
1143	ae_config.init |= INIT_SBPRO;
1144
1145	DBG(("done.\n"));
1146
1147	return TRUE;
1148}
1149
1150static void uninit_aedsp16_sb(void)
1151{
1152	DBG(("uninit_aedsp16_sb: "));
1153
1154	ae_config.init &= ~INIT_SBPRO;
1155
1156	DBG(("done.\n"));
1157}
1158
1159static int __init init_aedsp16_mss(void)
1160{
1161	DBG(("init_aedsp16_mss: "));
1162
1163/*
1164 * If the card is already init'ed SBPRO, we can not init it to MSS too
1165 * because the board can not emulate simultaneously MSS and SBPRO.
1166 */
1167	if (ae_config.init & INIT_SBPRO)
1168		return FALSE;
1169	if (ae_config.init & INIT_MSS)
1170		return FALSE;
1171/*
1172 * We must allocate the CONFIG_AEDSP16_BASE region too because these are the
1173 * I/O ports to access card's control registers.
1174 */
1175	if (!(ae_config.init & INIT_MPU401)) {
1176		if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
1177				"aedsp16 (base)")) {
1178			printk(
1179			"AEDSP16 BASE I/O port region is already in use.\n");
1180			return FALSE;
1181		}
1182	}
1183
1184	ae_config.init |= INIT_MSS;
1185
1186	DBG(("done.\n"));
1187
1188	return TRUE;
1189}
1190
1191static void uninit_aedsp16_mss(void)
1192{
1193	DBG(("uninit_aedsp16_mss: "));
1194
1195	if ((!(ae_config.init & INIT_MPU401)) &&
1196	   (ae_config.init & INIT_MSS)) {
1197		release_region(ae_config.base_io, IOBASE_REGION_SIZE);
1198		DBG(("AEDSP16 base region released.\n"));
1199	}
1200
1201	ae_config.init &= ~INIT_MSS;
1202	DBG(("done.\n"));
1203}
1204
1205static int __init init_aedsp16_mpu(void)
1206{
1207	DBG(("init_aedsp16_mpu: "));
1208
1209	if (ae_config.init & INIT_MPU401)
1210		return FALSE;
1211
1212/*
1213 * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O
1214 * ports to access card's control registers.
1215 */
1216	if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
1217		if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
1218					"aedsp16 (base)")) {
1219			printk(
1220			"AEDSP16 BASE I/O port region is already in use.\n");
1221			return FALSE;
1222		}
1223	}
1224
1225	ae_config.init |= INIT_MPU401;
1226
1227	DBG(("done.\n"));
1228
1229	return TRUE;
1230}
1231
1232static void uninit_aedsp16_mpu(void)
1233{
1234	DBG(("uninit_aedsp16_mpu: "));
1235
1236	if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
1237	   (ae_config.init & INIT_MPU401)) {
1238		release_region(ae_config.base_io, IOBASE_REGION_SIZE);
1239		DBG(("AEDSP16 base region released.\n"));
1240	}
1241
1242	ae_config.init &= ~INIT_MPU401;
1243
1244	DBG(("done.\n"));
1245}
1246
1247static int __init init_aedsp16(void)
1248{
1249	int initialized = FALSE;
1250
1251	DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
1252	     ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
1253
1254	if (ae_config.mss_base == -1) {
1255		if (init_aedsp16_sb() == FALSE) {
1256			uninit_aedsp16_sb();
1257		} else {
1258			initialized = TRUE;
1259		}
1260	}
1261
1262	if (ae_config.mpu_base != -1) {
1263		if (init_aedsp16_mpu() == FALSE) {
1264			uninit_aedsp16_mpu();
1265		} else {
1266			initialized = TRUE;
1267		}
1268	}
1269
1270/*
1271 * In the sequence of init routines, the MSS init MUST be the last!
1272 * This because of the special register programming the MSS mode needs.
1273 * A board reset would disable the MSS mode restoring the default SBPRO
1274 * mode.
1275 */
1276	if (ae_config.mss_base != -1) {
1277		if (init_aedsp16_mss() == FALSE) {
1278			uninit_aedsp16_mss();
1279		} else {
1280			initialized = TRUE;
1281		}
1282	}
1283
1284	if (initialized)
1285		initialized = aedsp16_init_board();
1286	return initialized;
1287}
1288
1289static void __exit uninit_aedsp16(void)
1290{
1291	if (ae_config.mss_base != -1)
1292		uninit_aedsp16_mss();
1293	else
1294		uninit_aedsp16_sb();
1295	if (ae_config.mpu_base != -1)
1296		uninit_aedsp16_mpu();
1297}
1298
1299static int __initdata io = -1;
1300static int __initdata irq = -1;
1301static int __initdata dma = -1;
1302static int __initdata mpu_irq = -1;
1303static int __initdata mss_base = -1;
1304static int __initdata mpu_base = -1;
1305
1306module_param(io, int, 0);
1307MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
1308module_param(irq, int, 0);
1309MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
1310module_param(dma, int, 0);
1311MODULE_PARM_DESC(dma, "dma line (0 1 3)");
1312module_param(mpu_irq, int, 0);
1313MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
1314module_param(mss_base, int, 0);
1315MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
1316module_param(mpu_base, int, 0);
1317MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
1318MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
1319MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
1320MODULE_LICENSE("GPL");
1321
1322static int __init do_init_aedsp16(void) {
1323	printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
1324	if (io == -1 || dma == -1 || irq == -1) {
1325		printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
1326		return -EINVAL;
1327	}
1328
1329	ae_config.base_io = io;
1330	ae_config.irq = irq;
1331	ae_config.dma = dma;
1332
1333	ae_config.mss_base = mss_base;
1334	ae_config.mpu_base = mpu_base;
1335	ae_config.mpu_irq = mpu_irq;
1336
1337	if (init_aedsp16() == FALSE) {
1338		printk(KERN_ERR "aedsp16: initialization failed\n");
1339		/*
1340		 * XXX
1341		 * What error should we return here ?
1342		 */
1343		return -EINVAL;
1344	}
1345	return 0;
1346}
1347
1348static void __exit cleanup_aedsp16(void) {
1349	uninit_aedsp16();
1350}
1351
1352module_init(do_init_aedsp16);
1353module_exit(cleanup_aedsp16);
1354
1355#ifndef MODULE
1356static int __init setup_aedsp16(char *str)
1357{
1358	/* io, irq, dma, mss_io, mpu_io, mpu_irq */
1359	int ints[7];
1360
1361	str = get_options(str, ARRAY_SIZE(ints), ints);
1362
1363	io	 = ints[1];
1364	irq	 = ints[2];
1365	dma	 = ints[3];
1366	mss_base = ints[4];
1367	mpu_base = ints[5];
1368	mpu_irq	 = ints[6];
1369	return 1;
1370}
1371
1372__setup("aedsp16=", setup_aedsp16);
1373#endif
1374