]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 <[email protected]> | |
22 | * 02-07-2003 Bug made it into first release. Take two. | |
23 | */ | |
24 | ||
1da177e4 LT |
25 | #include <linux/module.h> |
26 | #include <linux/moduleparam.h> | |
27 | #include <linux/init.h> | |
28 | #include "sound_config.h" | |
29 | #include "sb_mixer.h" | |
30 | #include "sb.h" | |
31 | #ifdef CONFIG_PNP | |
32 | #include <linux/pnp.h> | |
33 | #endif /* CONFIG_PNP */ | |
34 | #include "sb_card.h" | |
35 | ||
36 | MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver"); | |
37 | MODULE_LICENSE("GPL"); | |
38 | ||
39 | extern void *smw_free; | |
40 | ||
41 | static int __initdata mpu_io = 0; | |
42 | static int __initdata io = -1; | |
43 | static int __initdata irq = -1; | |
44 | static int __initdata dma = -1; | |
45 | static int __initdata dma16 = -1; | |
46 | static int __initdata type = 0; /* Can set this to a specific card type */ | |
47 | static int __initdata esstype = 0; /* ESS chip type */ | |
48 | static int __initdata acer = 0; /* Do acer notebook init? */ | |
49 | static int __initdata sm_games = 0; /* Logitech soundman games? */ | |
50 | ||
51 | static struct sb_card_config *legacy = NULL; | |
52 | ||
53 | #ifdef CONFIG_PNP | |
be54414d | 54 | static int pnp_registered; |
1da177e4 LT |
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 */ | |
be54414d | 136 | static int __init sb_init_legacy(void) |
1da177e4 LT |
137 | { |
138 | struct sb_module_options sbmo = {0}; | |
139 | ||
3159f06d | 140 | if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { |
1da177e4 LT |
141 | printk(KERN_ERR "sb: Error: Could not allocate memory\n"); |
142 | return -ENOMEM; | |
143 | } | |
1da177e4 LT |
144 | |
145 | legacy->conf.io_base = io; | |
146 | legacy->conf.irq = irq; | |
147 | legacy->conf.dma = dma; | |
148 | legacy->conf.dma2 = dma16; | |
149 | legacy->conf.card_subtype = type; | |
150 | ||
151 | legacy->mpucnf.io_base = mpu_io; | |
152 | legacy->mpucnf.irq = -1; | |
153 | legacy->mpucnf.dma = -1; | |
154 | legacy->mpucnf.dma2 = -1; | |
155 | ||
156 | sbmo.esstype = esstype; | |
157 | sbmo.sm_games = sm_games; | |
158 | sbmo.acer = acer; | |
159 | ||
160 | return sb_register_oss(legacy, &sbmo); | |
161 | } | |
162 | ||
163 | #ifdef CONFIG_PNP | |
164 | ||
165 | /* Populate the OSS subsystem structures with information from PnP */ | |
166 | static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc) | |
167 | { | |
168 | scc->conf.io_base = -1; | |
169 | scc->conf.irq = -1; | |
170 | scc->conf.dma = -1; | |
171 | scc->conf.dma2 = -1; | |
172 | scc->mpucnf.io_base = -1; | |
173 | scc->mpucnf.irq = -1; | |
174 | scc->mpucnf.dma = -1; | |
175 | scc->mpucnf.dma2 = -1; | |
176 | ||
177 | /* All clones layout their PnP tables differently and some use | |
178 | different logical devices for the MPU */ | |
179 | if(!strncmp("CTL",scc->card_id,3)) { | |
180 | scc->conf.io_base = pnp_port_start(dev,0); | |
181 | scc->conf.irq = pnp_irq(dev,0); | |
182 | scc->conf.dma = pnp_dma(dev,0); | |
183 | scc->conf.dma2 = pnp_dma(dev,1); | |
184 | scc->mpucnf.io_base = pnp_port_start(dev,1); | |
185 | return; | |
186 | } | |
187 | if(!strncmp("tBA",scc->card_id,3)) { | |
188 | scc->conf.io_base = pnp_port_start(dev,0); | |
189 | scc->conf.irq = pnp_irq(dev,0); | |
190 | scc->conf.dma = pnp_dma(dev,0); | |
191 | scc->conf.dma2 = pnp_dma(dev,1); | |
192 | return; | |
193 | } | |
194 | if(!strncmp("ESS",scc->card_id,3)) { | |
195 | scc->conf.io_base = pnp_port_start(dev,0); | |
196 | scc->conf.irq = pnp_irq(dev,0); | |
197 | scc->conf.dma = pnp_dma(dev,0); | |
198 | scc->conf.dma2 = pnp_dma(dev,1); | |
199 | scc->mpucnf.io_base = pnp_port_start(dev,2); | |
200 | return; | |
201 | } | |
202 | if(!strncmp("CMI",scc->card_id,3)) { | |
203 | scc->conf.io_base = pnp_port_start(dev,0); | |
204 | scc->conf.irq = pnp_irq(dev,0); | |
205 | scc->conf.dma = pnp_dma(dev,0); | |
206 | scc->conf.dma2 = pnp_dma(dev,1); | |
207 | return; | |
208 | } | |
209 | if(!strncmp("RWB",scc->card_id,3)) { | |
210 | scc->conf.io_base = pnp_port_start(dev,0); | |
211 | scc->conf.irq = pnp_irq(dev,0); | |
212 | scc->conf.dma = pnp_dma(dev,0); | |
213 | return; | |
214 | } | |
215 | if(!strncmp("ALS",scc->card_id,3)) { | |
216 | if(!strncmp("ALS0007",scc->card_id,7)) { | |
217 | scc->conf.io_base = pnp_port_start(dev,0); | |
218 | scc->conf.irq = pnp_irq(dev,0); | |
219 | scc->conf.dma = pnp_dma(dev,0); | |
220 | } else { | |
221 | scc->conf.io_base = pnp_port_start(dev,0); | |
222 | scc->conf.irq = pnp_irq(dev,0); | |
223 | scc->conf.dma = pnp_dma(dev,1); | |
224 | scc->conf.dma2 = pnp_dma(dev,0); | |
225 | } | |
226 | return; | |
227 | } | |
228 | if(!strncmp("RTL",scc->card_id,3)) { | |
229 | scc->conf.io_base = pnp_port_start(dev,0); | |
230 | scc->conf.irq = pnp_irq(dev,0); | |
231 | scc->conf.dma = pnp_dma(dev,1); | |
232 | scc->conf.dma2 = pnp_dma(dev,0); | |
233 | } | |
234 | } | |
235 | ||
be54414d BH |
236 | static unsigned int sb_pnp_devices; |
237 | ||
1da177e4 LT |
238 | /* Probe callback function for the PnP API */ |
239 | static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id) | |
240 | { | |
241 | struct sb_card_config *scc; | |
242 | struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */ | |
243 | struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL); | |
244 | ||
245 | if(!dev){ | |
246 | return -EBUSY; | |
247 | } | |
248 | ||
3159f06d | 249 | if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { |
1da177e4 LT |
250 | printk(KERN_ERR "sb: Error: Could not allocate memory\n"); |
251 | return -ENOMEM; | |
252 | } | |
1da177e4 LT |
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); | |
be54414d | 267 | sb_pnp_devices++; |
1da177e4 LT |
268 | |
269 | return sb_register_oss(scc, &sbmo); | |
270 | } | |
271 | ||
272 | static void sb_pnp_remove(struct pnp_card_link *card) | |
273 | { | |
274 | struct sb_card_config *scc = pnp_get_card_drvdata(card); | |
275 | ||
276 | if(!scc) | |
277 | return; | |
278 | ||
279 | printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id); | |
280 | ||
281 | sb_unload(scc); | |
282 | } | |
283 | ||
284 | static struct pnp_card_driver sb_pnp_driver = { | |
285 | .name = "OSS SndBlstr", /* 16 character limit */ | |
286 | .id_table = sb_pnp_card_table, | |
287 | .probe = sb_pnp_probe, | |
288 | .remove = sb_pnp_remove, | |
289 | }; | |
290 | MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table); | |
291 | #endif /* CONFIG_PNP */ | |
292 | ||
10c86be5 | 293 | static void sb_unregister_all(void) |
be54414d BH |
294 | { |
295 | #ifdef CONFIG_PNP | |
296 | if (pnp_registered) | |
297 | pnp_unregister_card_driver(&sb_pnp_driver); | |
298 | #endif | |
299 | } | |
300 | ||
1da177e4 LT |
301 | static int __init sb_init(void) |
302 | { | |
303 | int lres = 0; | |
304 | int pres = 0; | |
305 | ||
306 | printk(KERN_INFO "sb: Init: Starting Probe...\n"); | |
307 | ||
308 | if(io != -1 && irq != -1 && dma != -1) { | |
309 | printk(KERN_INFO "sb: Probing legacy card with io=%x, "\ | |
310 | "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16); | |
311 | lres = sb_init_legacy(); | |
312 | } else if((io != -1 || irq != -1 || dma != -1) || | |
313 | (!pnp && (io == -1 && irq == -1 && dma == -1))) | |
314 | printk(KERN_ERR "sb: Error: At least io, irq, and dma "\ | |
315 | "must be set for legacy cards.\n"); | |
316 | ||
317 | #ifdef CONFIG_PNP | |
318 | if(pnp) { | |
be54414d BH |
319 | int err = pnp_register_card_driver(&sb_pnp_driver); |
320 | if (!err) | |
321 | pnp_registered = 1; | |
322 | pres = sb_pnp_devices; | |
1da177e4 LT |
323 | } |
324 | #endif | |
325 | printk(KERN_INFO "sb: Init: Done\n"); | |
326 | ||
327 | /* If either PnP or Legacy registered a card then return | |
328 | * success */ | |
be54414d BH |
329 | if (pres == 0 && lres <= 0) { |
330 | sb_unregister_all(); | |
1da177e4 LT |
331 | return -ENODEV; |
332 | } | |
333 | return 0; | |
334 | } | |
335 | ||
336 | static void __exit sb_exit(void) | |
337 | { | |
338 | printk(KERN_INFO "sb: Unloading...\n"); | |
339 | ||
340 | /* Unload legacy card */ | |
341 | if (legacy) { | |
342 | printk (KERN_INFO "sb: Unloading legacy card\n"); | |
343 | sb_unload(legacy); | |
344 | } | |
345 | ||
be54414d | 346 | sb_unregister_all(); |
1da177e4 | 347 | |
457d3d43 JJ |
348 | vfree(smw_free); |
349 | smw_free = NULL; | |
1da177e4 LT |
350 | } |
351 | ||
352 | module_init(sb_init); | |
353 | module_exit(sb_exit); |