1/*
2 * sound/sgalaxy.c
3 *
4 * Low level driver for Aztech Sound Galaxy cards.
5 * Copyright 1998 Artur Skawina <skawina@geocities.com>
6 *
7 * Supported cards:
8 *    Aztech Sound Galaxy Waverider Pro 32 - 3D
9 *    Aztech Sound Galaxy Washington 16
10 *
11 * Based on cs4232.c by Hannu Savolainen and Alan Cox.
12 *
13 *
14 * Copyright (C) by Hannu Savolainen 1993-1997
15 *
16 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
17 * Version 2 (June 1991). See the "COPYING" file distributed with this software
18 * for more info.
19 *
20 * Changes:
21 * 11-10-2000	Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
22 *		Added __init to sb_rst() and sb_cmd()
23 */
24
25#include <linux/init.h>
26#include <linux/module.h>
27
28#include "sound_config.h"
29#include "ad1848.h"
30
31static void sleep( unsigned howlong )
32{
33	current->state   = TASK_INTERRUPTIBLE;
34	schedule_timeout(howlong);
35}
36
37#define DPORT 0x80
38
39/* Sound Blaster regs */
40
41#define SBDSP_RESET      0x6
42#define SBDSP_READ       0xA
43#define SBDSP_COMMAND    0xC
44#define SBDSP_STATUS     SBDSP_COMMAND
45#define SBDSP_DATA_AVAIL 0xE
46
47static int __init sb_rst(int base)
48{
49	int   i;
50
51	outb( 1, base+SBDSP_RESET );     /* reset the DSP */
52	outb( 0, base+SBDSP_RESET );
53
54	for ( i=0; i<500; i++ )          /* delay */
55		inb(DPORT);
56
57	for ( i=0; i<100000; i++ )
58	{
59		if ( inb( base+SBDSP_DATA_AVAIL )&0x80 )
60			break;
61	}
62
63	if ( inb( base+SBDSP_READ )!=0xAA )
64		return 0;
65
66	return 1;
67}
68
69static int __init sb_cmd( int base, unsigned char val )
70{
71	int  i;
72
73	for ( i=100000; i; i-- )
74	{
75		if ( (inb( base+SBDSP_STATUS )&0x80)==0 )
76		{
77        		outb( val, base+SBDSP_COMMAND );
78        		break;
79		}
80	}
81	return i;      /* i>0 == success */
82}
83
84
85#define ai_sgbase    driver_use_1
86
87static int __init probe_sgalaxy( struct address_info *ai )
88{
89	if ( check_region( ai->io_base, 8 ) ) {
90		printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base);
91		return 0;
92	}
93
94	if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) )
95		return probe_ms_sound(ai);  /* The card is already active, check irq etc... */
96
97	if ( check_region( ai->ai_sgbase, 0x10 ) ) {
98		printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase);
99		return 0;
100	}
101
102	/* switch to MSS/WSS mode */
103
104	sb_rst( ai->ai_sgbase );
105
106	sb_cmd( ai->ai_sgbase, 9 );
107	sb_cmd( ai->ai_sgbase, 0 );
108
109	sleep( HZ/10 );
110
111      	return probe_ms_sound(ai);
112}
113
114static void __init attach_sgalaxy( struct address_info *ai )
115{
116	int n;
117
118	request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" );
119
120	attach_ms_sound(ai, THIS_MODULE);
121	n=ai->slots[0];
122
123	if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) {
124		AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE );   /* Line-in */
125		AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH );  /* FM+Wavetable*/
126		AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD );     /* CD */
127	}
128}
129
130static void __exit unload_sgalaxy( struct address_info *ai )
131{
132	unload_ms_sound( ai );
133	release_region( ai->ai_sgbase, 0x10 );
134}
135
136static struct address_info cfg;
137
138static int __initdata io	= -1;
139static int __initdata irq	= -1;
140static int __initdata dma	= -1;
141static int __initdata dma2	= -1;
142static int __initdata sgbase	= -1;
143
144MODULE_PARM(io,"i");
145MODULE_PARM(irq,"i");
146MODULE_PARM(dma,"i");
147MODULE_PARM(dma2,"i");
148MODULE_PARM(sgbase,"i");
149
150static int __init init_sgalaxy(void)
151{
152	cfg.io_base   = io;
153	cfg.irq       = irq;
154	cfg.dma       = dma;
155	cfg.dma2      = dma2;
156	cfg.ai_sgbase = sgbase;
157
158	if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) {
159		printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n");
160		return -EINVAL;
161	}
162
163	if ( probe_sgalaxy(&cfg) == 0 )
164		return -ENODEV;
165
166	attach_sgalaxy(&cfg);
167
168	return 0;
169}
170
171static void __exit cleanup_sgalaxy(void)
172{
173	unload_sgalaxy(&cfg);
174}
175
176module_init(init_sgalaxy);
177module_exit(cleanup_sgalaxy);
178
179#ifndef MODULE
180static int __init setup_sgalaxy(char *str)
181{
182	/* io, irq, dma, dma2, sgbase */
183	int ints[6];
184
185	str = get_options(str, ARRAY_SIZE(ints), ints);
186	io	= ints[1];
187	irq	= ints[2];
188	dma	= ints[3];
189	dma2	= ints[4];
190	sgbase	= ints[5];
191
192	return 1;
193}
194
195__setup("sgalaxy=", setup_sgalaxy);
196#endif
197MODULE_LICENSE("GPL");
198