diff options
Diffstat (limited to 'sound/oss/sb_card.c')
-rw-r--r-- | sound/oss/sb_card.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c new file mode 100644 index 000000000000..680b82e15298 --- /dev/null +++ b/sound/oss/sb_card.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * sound/oss/sb_card.c | ||
3 | * | ||
4 | * Detection routine for the ISA Sound Blaster and compatable sound | ||
5 | * cards. | ||
6 | * | ||
7 | * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) | ||
8 | * Version 2 (June 1991). See the "COPYING" file distributed with this | ||
9 | * software for more info. | ||
10 | * | ||
11 | * This is a complete rewrite of the detection routines. This was | ||
12 | * prompted by the PnP API change during v2.5 and the ugly state the | ||
13 | * code was in. | ||
14 | * | ||
15 | * Copyright (C) by Paul Laufer 2002. Based on code originally by | ||
16 | * Hannu Savolainen which was modified by many others over the | ||
17 | * years. Authors specifically mentioned in the previous version were: | ||
18 | * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de | ||
19 | * Melo, Daniel Church, and myself. | ||
20 | * | ||
21 | * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com> | ||
22 | * 02-07-2003 Bug made it into first release. Take two. | ||
23 | */ | ||
24 | |||
25 | #include <linux/config.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/init.h> | ||
29 | #include "sound_config.h" | ||
30 | #include "sb_mixer.h" | ||
31 | #include "sb.h" | ||
32 | #ifdef CONFIG_PNP | ||
33 | #include <linux/pnp.h> | ||
34 | #endif /* CONFIG_PNP */ | ||
35 | #include "sb_card.h" | ||
36 | |||
37 | MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | extern void *smw_free; | ||
41 | |||
42 | static int __initdata mpu_io = 0; | ||
43 | static int __initdata io = -1; | ||
44 | static int __initdata irq = -1; | ||
45 | static int __initdata dma = -1; | ||
46 | static int __initdata dma16 = -1; | ||
47 | static int __initdata type = 0; /* Can set this to a specific card type */ | ||
48 | static int __initdata esstype = 0; /* ESS chip type */ | ||
49 | static int __initdata acer = 0; /* Do acer notebook init? */ | ||
50 | static int __initdata sm_games = 0; /* Logitech soundman games? */ | ||
51 | |||
52 | static struct sb_card_config *legacy = NULL; | ||
53 | |||
54 | #ifdef CONFIG_PNP | ||
55 | static int __initdata pnp = 1; | ||
56 | /* | ||
57 | static int __initdata uart401 = 0; | ||
58 | */ | ||
59 | #else | ||
60 | static int __initdata pnp = 0; | ||
61 | #endif | ||
62 | |||
63 | module_param(io, int, 000); | ||
64 | MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); | ||
65 | module_param(irq, int, 000); | ||
66 | MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); | ||
67 | module_param(dma, int, 000); | ||
68 | MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); | ||
69 | module_param(dma16, int, 000); | ||
70 | MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); | ||
71 | module_param(mpu_io, int, 000); | ||
72 | MODULE_PARM_DESC(mpu_io, "MPU base address"); | ||
73 | module_param(type, int, 000); | ||
74 | MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \ | ||
75 | "work with pnp)"); | ||
76 | module_param(sm_games, int, 000); | ||
77 | MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \ | ||
78 | "(doesn't work with pnp)"); | ||
79 | module_param(esstype, int, 000); | ||
80 | MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)"); | ||
81 | module_param(acer, int, 000); | ||
82 | MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\ | ||
83 | "(doesn't work with pnp)"); | ||
84 | |||
85 | #ifdef CONFIG_PNP | ||
86 | module_param(pnp, int, 000); | ||
87 | MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\ | ||
88 | "Default is 1.\n"); | ||
89 | /* Not done yet.... */ | ||
90 | /* | ||
91 | module_param(uart401, int, 000); | ||
92 | MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\ | ||
93 | "the mpu on some clones"); | ||
94 | */ | ||
95 | #endif /* CONFIG_PNP */ | ||
96 | |||
97 | /* OSS subsystem card registration shared by PnP and legacy routines */ | ||
98 | static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo) | ||
99 | { | ||
100 | if (!request_region(scc->conf.io_base, 16, "soundblaster")) { | ||
101 | printk(KERN_ERR "sb: ports busy.\n"); | ||
102 | kfree(scc); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | |||
106 | if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) { | ||
107 | release_region(scc->conf.io_base, 16); | ||
108 | printk(KERN_ERR "sb: Failed DSP Detect.\n"); | ||
109 | kfree(scc); | ||
110 | return -ENODEV; | ||
111 | } | ||
112 | if(!sb_dsp_init(&scc->conf, THIS_MODULE)) { | ||
113 | printk(KERN_ERR "sb: Failed DSP init.\n"); | ||
114 | kfree(scc); | ||
115 | return -ENODEV; | ||
116 | } | ||
117 | if(scc->mpucnf.io_base > 0) { | ||
118 | scc->mpu = 1; | ||
119 | printk(KERN_INFO "sb: Turning on MPU\n"); | ||
120 | if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE)) | ||
121 | scc->mpu = 0; | ||
122 | } | ||
123 | |||
124 | return 1; | ||
125 | } | ||
126 | |||
127 | static void sb_unload(struct sb_card_config *scc) | ||
128 | { | ||
129 | sb_dsp_unload(&scc->conf, 0); | ||
130 | if(scc->mpu) | ||
131 | unload_sbmpu(&scc->mpucnf); | ||
132 | kfree(scc); | ||
133 | } | ||
134 | |||
135 | /* Register legacy card with OSS subsystem */ | ||
136 | static int sb_init_legacy(void) | ||
137 | { | ||
138 | struct sb_module_options sbmo = {0}; | ||
139 | |||
140 | if((legacy = kmalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { | ||
141 | printk(KERN_ERR "sb: Error: Could not allocate memory\n"); | ||
142 | return -ENOMEM; | ||
143 | } | ||
144 | memset(legacy, 0, sizeof(struct sb_card_config)); | ||
145 | |||
146 | legacy->conf.io_base = io; | ||
147 | legacy->conf.irq = irq; | ||
148 | legacy->conf.dma = dma; | ||
149 | legacy->conf.dma2 = dma16; | ||
150 | legacy->conf.card_subtype = type; | ||
151 | |||
152 | legacy->mpucnf.io_base = mpu_io; | ||
153 | legacy->mpucnf.irq = -1; | ||
154 | legacy->mpucnf.dma = -1; | ||
155 | legacy->mpucnf.dma2 = -1; | ||
156 | |||
157 | sbmo.esstype = esstype; | ||
158 | sbmo.sm_games = sm_games; | ||
159 | sbmo.acer = acer; | ||
160 | |||
161 | return sb_register_oss(legacy, &sbmo); | ||
162 | } | ||
163 | |||
164 | #ifdef CONFIG_PNP | ||
165 | |||
166 | /* Populate the OSS subsystem structures with information from PnP */ | ||
167 | static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc) | ||
168 | { | ||
169 | scc->conf.io_base = -1; | ||
170 | scc->conf.irq = -1; | ||
171 | scc->conf.dma = -1; | ||
172 | scc->conf.dma2 = -1; | ||
173 | scc->mpucnf.io_base = -1; | ||
174 | scc->mpucnf.irq = -1; | ||
175 | scc->mpucnf.dma = -1; | ||
176 | scc->mpucnf.dma2 = -1; | ||
177 | |||
178 | /* All clones layout their PnP tables differently and some use | ||
179 | different logical devices for the MPU */ | ||
180 | if(!strncmp("CTL",scc->card_id,3)) { | ||
181 | scc->conf.io_base = pnp_port_start(dev,0); | ||
182 | scc->conf.irq = pnp_irq(dev,0); | ||
183 | scc->conf.dma = pnp_dma(dev,0); | ||
184 | scc->conf.dma2 = pnp_dma(dev,1); | ||
185 | scc->mpucnf.io_base = pnp_port_start(dev,1); | ||
186 | return; | ||
187 | } | ||
188 | if(!strncmp("tBA",scc->card_id,3)) { | ||
189 | scc->conf.io_base = pnp_port_start(dev,0); | ||
190 | scc->conf.irq = pnp_irq(dev,0); | ||
191 | scc->conf.dma = pnp_dma(dev,0); | ||
192 | scc->conf.dma2 = pnp_dma(dev,1); | ||
193 | return; | ||
194 | } | ||
195 | if(!strncmp("ESS",scc->card_id,3)) { | ||
196 | scc->conf.io_base = pnp_port_start(dev,0); | ||
197 | scc->conf.irq = pnp_irq(dev,0); | ||
198 | scc->conf.dma = pnp_dma(dev,0); | ||
199 | scc->conf.dma2 = pnp_dma(dev,1); | ||
200 | scc->mpucnf.io_base = pnp_port_start(dev,2); | ||
201 | return; | ||
202 | } | ||
203 | if(!strncmp("CMI",scc->card_id,3)) { | ||
204 | scc->conf.io_base = pnp_port_start(dev,0); | ||
205 | scc->conf.irq = pnp_irq(dev,0); | ||
206 | scc->conf.dma = pnp_dma(dev,0); | ||
207 | scc->conf.dma2 = pnp_dma(dev,1); | ||
208 | return; | ||
209 | } | ||
210 | if(!strncmp("RWB",scc->card_id,3)) { | ||
211 | scc->conf.io_base = pnp_port_start(dev,0); | ||
212 | scc->conf.irq = pnp_irq(dev,0); | ||
213 | scc->conf.dma = pnp_dma(dev,0); | ||
214 | return; | ||
215 | } | ||
216 | if(!strncmp("ALS",scc->card_id,3)) { | ||
217 | if(!strncmp("ALS0007",scc->card_id,7)) { | ||
218 | scc->conf.io_base = pnp_port_start(dev,0); | ||
219 | scc->conf.irq = pnp_irq(dev,0); | ||
220 | scc->conf.dma = pnp_dma(dev,0); | ||
221 | } else { | ||
222 | scc->conf.io_base = pnp_port_start(dev,0); | ||
223 | scc->conf.irq = pnp_irq(dev,0); | ||
224 | scc->conf.dma = pnp_dma(dev,1); | ||
225 | scc->conf.dma2 = pnp_dma(dev,0); | ||
226 | } | ||
227 | return; | ||
228 | } | ||
229 | if(!strncmp("RTL",scc->card_id,3)) { | ||
230 | scc->conf.io_base = pnp_port_start(dev,0); | ||
231 | scc->conf.irq = pnp_irq(dev,0); | ||
232 | scc->conf.dma = pnp_dma(dev,1); | ||
233 | scc->conf.dma2 = pnp_dma(dev,0); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /* Probe callback function for the PnP API */ | ||
238 | static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id) | ||
239 | { | ||
240 | struct sb_card_config *scc; | ||
241 | struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */ | ||
242 | struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL); | ||
243 | |||
244 | if(!dev){ | ||
245 | return -EBUSY; | ||
246 | } | ||
247 | |||
248 | if((scc = kmalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { | ||
249 | printk(KERN_ERR "sb: Error: Could not allocate memory\n"); | ||
250 | return -ENOMEM; | ||
251 | } | ||
252 | memset(scc, 0, sizeof(struct sb_card_config)); | ||
253 | |||
254 | printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \ | ||
255 | "%s, Device PnP id = %s\n", card->card->name, card_id->id, | ||
256 | dev->id->id); | ||
257 | |||
258 | scc->card_id = card_id->id; | ||
259 | scc->dev_id = dev->id->id; | ||
260 | sb_dev2cfg(dev, scc); | ||
261 | |||
262 | printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \ | ||
263 | "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq, | ||
264 | scc->conf.dma, scc->conf.dma2); | ||
265 | |||
266 | pnp_set_card_drvdata(card, scc); | ||
267 | |||
268 | return sb_register_oss(scc, &sbmo); | ||
269 | } | ||
270 | |||
271 | static void sb_pnp_remove(struct pnp_card_link *card) | ||
272 | { | ||
273 | struct sb_card_config *scc = pnp_get_card_drvdata(card); | ||
274 | |||
275 | if(!scc) | ||
276 | return; | ||
277 | |||
278 | printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id); | ||
279 | |||
280 | sb_unload(scc); | ||
281 | } | ||
282 | |||
283 | static struct pnp_card_driver sb_pnp_driver = { | ||
284 | .name = "OSS SndBlstr", /* 16 character limit */ | ||
285 | .id_table = sb_pnp_card_table, | ||
286 | .probe = sb_pnp_probe, | ||
287 | .remove = sb_pnp_remove, | ||
288 | }; | ||
289 | MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table); | ||
290 | #endif /* CONFIG_PNP */ | ||
291 | |||
292 | static int __init sb_init(void) | ||
293 | { | ||
294 | int lres = 0; | ||
295 | int pres = 0; | ||
296 | |||
297 | printk(KERN_INFO "sb: Init: Starting Probe...\n"); | ||
298 | |||
299 | if(io != -1 && irq != -1 && dma != -1) { | ||
300 | printk(KERN_INFO "sb: Probing legacy card with io=%x, "\ | ||
301 | "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16); | ||
302 | lres = sb_init_legacy(); | ||
303 | } else if((io != -1 || irq != -1 || dma != -1) || | ||
304 | (!pnp && (io == -1 && irq == -1 && dma == -1))) | ||
305 | printk(KERN_ERR "sb: Error: At least io, irq, and dma "\ | ||
306 | "must be set for legacy cards.\n"); | ||
307 | |||
308 | #ifdef CONFIG_PNP | ||
309 | if(pnp) { | ||
310 | pres = pnp_register_card_driver(&sb_pnp_driver); | ||
311 | } | ||
312 | #endif | ||
313 | printk(KERN_INFO "sb: Init: Done\n"); | ||
314 | |||
315 | /* If either PnP or Legacy registered a card then return | ||
316 | * success */ | ||
317 | if (pres <= 0 && lres <= 0) { | ||
318 | #ifdef CONFIG_PNP | ||
319 | pnp_unregister_card_driver(&sb_pnp_driver); | ||
320 | #endif | ||
321 | return -ENODEV; | ||
322 | } | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static void __exit sb_exit(void) | ||
327 | { | ||
328 | printk(KERN_INFO "sb: Unloading...\n"); | ||
329 | |||
330 | /* Unload legacy card */ | ||
331 | if (legacy) { | ||
332 | printk (KERN_INFO "sb: Unloading legacy card\n"); | ||
333 | sb_unload(legacy); | ||
334 | } | ||
335 | |||
336 | #ifdef CONFIG_PNP | ||
337 | pnp_unregister_card_driver(&sb_pnp_driver); | ||
338 | #endif | ||
339 | |||
340 | if (smw_free) { | ||
341 | vfree(smw_free); | ||
342 | smw_free = NULL; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | module_init(sb_init); | ||
347 | module_exit(sb_exit); | ||