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 <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); |