]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * sound/oss/sb_card.c | |
3 | * | |
25985edc | 4 | * Detection routine for the ISA Sound Blaster and compatible sound |
1da177e4 LT |
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> | |
5a0e3ad6 | 27 | #include <linux/slab.h> |
1da177e4 LT |
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 | |
be54414d | 55 | static int pnp_registered; |
1da177e4 LT |
56 | static int __initdata pnp = 1; |
57 | /* | |
58 | static int __initdata uart401 = 0; | |
59 | */ | |
60 | #else | |
61 | static int __initdata pnp = 0; | |
62 | #endif | |
63 | ||
64 | module_param(io, int, 000); | |
65 | MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); | |
66 | module_param(irq, int, 000); | |
67 | MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); | |
68 | module_param(dma, int, 000); | |
69 | MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); | |
70 | module_param(dma16, int, 000); | |
71 | MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); | |
72 | module_param(mpu_io, int, 000); | |
73 | MODULE_PARM_DESC(mpu_io, "MPU base address"); | |
74 | module_param(type, int, 000); | |
75 | MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \ | |
76 | "work with pnp)"); | |
77 | module_param(sm_games, int, 000); | |
78 | MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \ | |
79 | "(doesn't work with pnp)"); | |
80 | module_param(esstype, int, 000); | |
81 | MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)"); | |
82 | module_param(acer, int, 000); | |
83 | MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\ | |
84 | "(doesn't work with pnp)"); | |
85 | ||
86 | #ifdef CONFIG_PNP | |
87 | module_param(pnp, int, 000); | |
88 | MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\ | |
89 | "Default is 1.\n"); | |
90 | /* Not done yet.... */ | |
91 | /* | |
92 | module_param(uart401, int, 000); | |
93 | MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\ | |
94 | "the mpu on some clones"); | |
95 | */ | |
96 | #endif /* CONFIG_PNP */ | |
97 | ||
98 | /* OSS subsystem card registration shared by PnP and legacy routines */ | |
99 | static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo) | |
100 | { | |
101 | if (!request_region(scc->conf.io_base, 16, "soundblaster")) { | |
102 | printk(KERN_ERR "sb: ports busy.\n"); | |
103 | kfree(scc); | |
104 | return -EBUSY; | |
105 | } | |
106 | ||
107 | if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) { | |
108 | release_region(scc->conf.io_base, 16); | |
109 | printk(KERN_ERR "sb: Failed DSP Detect.\n"); | |
110 | kfree(scc); | |
111 | return -ENODEV; | |
112 | } | |
113 | if(!sb_dsp_init(&scc->conf, THIS_MODULE)) { | |
114 | printk(KERN_ERR "sb: Failed DSP init.\n"); | |
115 | kfree(scc); | |
116 | return -ENODEV; | |
117 | } | |
118 | if(scc->mpucnf.io_base > 0) { | |
119 | scc->mpu = 1; | |
120 | printk(KERN_INFO "sb: Turning on MPU\n"); | |
121 | if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE)) | |
122 | scc->mpu = 0; | |
123 | } | |
124 | ||
125 | return 1; | |
126 | } | |
127 | ||
128 | static void sb_unload(struct sb_card_config *scc) | |
129 | { | |
130 | sb_dsp_unload(&scc->conf, 0); | |
131 | if(scc->mpu) | |
132 | unload_sbmpu(&scc->mpucnf); | |
133 | kfree(scc); | |
134 | } | |
135 | ||
136 | /* Register legacy card with OSS subsystem */ | |
be54414d | 137 | static int __init sb_init_legacy(void) |
1da177e4 LT |
138 | { |
139 | struct sb_module_options sbmo = {0}; | |
140 | ||
3159f06d | 141 | if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { |
1da177e4 LT |
142 | printk(KERN_ERR "sb: Error: Could not allocate memory\n"); |
143 | return -ENOMEM; | |
144 | } | |
1da177e4 LT |
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 | ||
be54414d BH |
237 | static unsigned int sb_pnp_devices; |
238 | ||
1da177e4 LT |
239 | /* Probe callback function for the PnP API */ |
240 | static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id) | |
241 | { | |
242 | struct sb_card_config *scc; | |
243 | struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */ | |
244 | struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL); | |
245 | ||
246 | if(!dev){ | |
247 | return -EBUSY; | |
248 | } | |
249 | ||
3159f06d | 250 | if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { |
1da177e4 LT |
251 | printk(KERN_ERR "sb: Error: Could not allocate memory\n"); |
252 | return -ENOMEM; | |
253 | } | |
1da177e4 LT |
254 | |
255 | printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \ | |
256 | "%s, Device PnP id = %s\n", card->card->name, card_id->id, | |
257 | dev->id->id); | |
258 | ||
259 | scc->card_id = card_id->id; | |
260 | scc->dev_id = dev->id->id; | |
261 | sb_dev2cfg(dev, scc); | |
262 | ||
263 | printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \ | |
264 | "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq, | |
265 | scc->conf.dma, scc->conf.dma2); | |
266 | ||
267 | pnp_set_card_drvdata(card, scc); | |
be54414d | 268 | sb_pnp_devices++; |
1da177e4 LT |
269 | |
270 | return sb_register_oss(scc, &sbmo); | |
271 | } | |
272 | ||
273 | static void sb_pnp_remove(struct pnp_card_link *card) | |
274 | { | |
275 | struct sb_card_config *scc = pnp_get_card_drvdata(card); | |
276 | ||
277 | if(!scc) | |
278 | return; | |
279 | ||
280 | printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id); | |
281 | ||
282 | sb_unload(scc); | |
283 | } | |
284 | ||
285 | static struct pnp_card_driver sb_pnp_driver = { | |
286 | .name = "OSS SndBlstr", /* 16 character limit */ | |
287 | .id_table = sb_pnp_card_table, | |
288 | .probe = sb_pnp_probe, | |
289 | .remove = sb_pnp_remove, | |
290 | }; | |
291 | MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table); | |
292 | #endif /* CONFIG_PNP */ | |
293 | ||
10c86be5 | 294 | static void sb_unregister_all(void) |
be54414d BH |
295 | { |
296 | #ifdef CONFIG_PNP | |
297 | if (pnp_registered) | |
298 | pnp_unregister_card_driver(&sb_pnp_driver); | |
299 | #endif | |
300 | } | |
301 | ||
1da177e4 LT |
302 | static int __init sb_init(void) |
303 | { | |
304 | int lres = 0; | |
305 | int pres = 0; | |
306 | ||
307 | printk(KERN_INFO "sb: Init: Starting Probe...\n"); | |
308 | ||
309 | if(io != -1 && irq != -1 && dma != -1) { | |
310 | printk(KERN_INFO "sb: Probing legacy card with io=%x, "\ | |
311 | "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16); | |
312 | lres = sb_init_legacy(); | |
313 | } else if((io != -1 || irq != -1 || dma != -1) || | |
314 | (!pnp && (io == -1 && irq == -1 && dma == -1))) | |
315 | printk(KERN_ERR "sb: Error: At least io, irq, and dma "\ | |
316 | "must be set for legacy cards.\n"); | |
317 | ||
318 | #ifdef CONFIG_PNP | |
319 | if(pnp) { | |
be54414d BH |
320 | int err = pnp_register_card_driver(&sb_pnp_driver); |
321 | if (!err) | |
322 | pnp_registered = 1; | |
323 | pres = sb_pnp_devices; | |
1da177e4 LT |
324 | } |
325 | #endif | |
326 | printk(KERN_INFO "sb: Init: Done\n"); | |
327 | ||
328 | /* If either PnP or Legacy registered a card then return | |
329 | * success */ | |
be54414d BH |
330 | if (pres == 0 && lres <= 0) { |
331 | sb_unregister_all(); | |
1da177e4 LT |
332 | return -ENODEV; |
333 | } | |
334 | return 0; | |
335 | } | |
336 | ||
337 | static void __exit sb_exit(void) | |
338 | { | |
339 | printk(KERN_INFO "sb: Unloading...\n"); | |
340 | ||
341 | /* Unload legacy card */ | |
342 | if (legacy) { | |
343 | printk (KERN_INFO "sb: Unloading legacy card\n"); | |
344 | sb_unload(legacy); | |
345 | } | |
346 | ||
be54414d | 347 | sb_unregister_all(); |
1da177e4 | 348 | |
457d3d43 JJ |
349 | vfree(smw_free); |
350 | smw_free = NULL; | |
1da177e4 LT |
351 | } |
352 | ||
353 | module_init(sb_init); | |
354 | module_exit(sb_exit); |