diff options
Diffstat (limited to 'sound/isa/sc6000.c')
-rw-r--r-- | sound/isa/sc6000.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c new file mode 100644 index 000000000000..94daf8399994 --- /dev/null +++ b/sound/isa/sc6000.c | |||
@@ -0,0 +1,656 @@ | |||
1 | /* | ||
2 | * Driver for Gallant SC-6000 soundcard. This card is also known as | ||
3 | * Audio Excel DSP 16 or Zoltrix AV302. | ||
4 | * These cards use CompuMedia ASC-9308 chip + AD1848 codec. | ||
5 | * | ||
6 | * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> | ||
7 | * | ||
8 | * I don't have documentation for this card. I used the driver | ||
9 | * for OSS/Free included in the kernel source as reference. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <sound/driver.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/isa.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <asm/dma.h> | ||
32 | #include <sound/core.h> | ||
33 | #include <sound/ad1848.h> | ||
34 | #include <sound/opl3.h> | ||
35 | #include <sound/mpu401.h> | ||
36 | #include <sound/control.h> | ||
37 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
38 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
39 | #include <sound/initval.h> | ||
40 | |||
41 | MODULE_AUTHOR("Krzysztof Helt"); | ||
42 | MODULE_DESCRIPTION("Gallant SC-6000"); | ||
43 | MODULE_LICENSE("GPL"); | ||
44 | MODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000}," | ||
45 | "{AudioExcel, Audio Excel DSP 16}," | ||
46 | "{Zoltrix, AV302}}"); | ||
47 | |||
48 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
49 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
50 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
51 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */ | ||
52 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 11 */ | ||
53 | static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */ | ||
54 | static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
55 | /* 0x300, 0x310, 0x320, 0x330 */ | ||
56 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */ | ||
57 | static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */ | ||
58 | |||
59 | module_param_array(index, int, NULL, 0444); | ||
60 | MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard."); | ||
61 | module_param_array(id, charp, NULL, 0444); | ||
62 | MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard."); | ||
63 | module_param_array(enable, bool, NULL, 0444); | ||
64 | MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard."); | ||
65 | module_param_array(port, long, NULL, 0444); | ||
66 | MODULE_PARM_DESC(port, "Port # for sc-6000 driver."); | ||
67 | module_param_array(mss_port, long, NULL, 0444); | ||
68 | MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver."); | ||
69 | module_param_array(mpu_port, long, NULL, 0444); | ||
70 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver."); | ||
71 | module_param_array(irq, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver."); | ||
73 | module_param_array(mpu_irq, int, NULL, 0444); | ||
74 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver."); | ||
75 | module_param_array(dma, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver."); | ||
77 | |||
78 | /* | ||
79 | * Commands of SC6000's DSP (SBPRO+special). | ||
80 | * Some of them are COMMAND_xx, in the future they may change. | ||
81 | */ | ||
82 | #define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ | ||
83 | #define COMMAND_52 0x52 /* */ | ||
84 | #define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ | ||
85 | #define COMMAND_5C 0x5c /* */ | ||
86 | #define COMMAND_60 0x60 /* */ | ||
87 | #define COMMAND_66 0x66 /* */ | ||
88 | #define COMMAND_6C 0x6c /* */ | ||
89 | #define COMMAND_6E 0x6e /* */ | ||
90 | #define COMMAND_88 0x88 /* Unknown command */ | ||
91 | #define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ | ||
92 | #define COMMAND_C5 0xc5 /* */ | ||
93 | #define GET_DSP_VERSION 0xe1 /* Get DSP Version */ | ||
94 | #define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ | ||
95 | |||
96 | /* | ||
97 | * Offsets of SC6000 DSP I/O ports. The offset is added to base I/O port | ||
98 | * to have the actual I/O port. | ||
99 | * Register permissions are: | ||
100 | * (wo) == Write Only | ||
101 | * (ro) == Read Only | ||
102 | * (w-) == Write | ||
103 | * (r-) == Read | ||
104 | */ | ||
105 | #define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ | ||
106 | #define DSP_READ 0x0a /* offset of DSP READ (ro) */ | ||
107 | #define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ | ||
108 | #define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ | ||
109 | #define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ | ||
110 | #define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ | ||
111 | |||
112 | #define PFX "sc6000: " | ||
113 | #define DRV_NAME "SC-6000" | ||
114 | |||
115 | /* hardware dependent functions */ | ||
116 | |||
117 | /* | ||
118 | * sc6000_irq_to_softcfg - Decode irq number into cfg code. | ||
119 | */ | ||
120 | static __devinit unsigned char sc6000_irq_to_softcfg(int irq) | ||
121 | { | ||
122 | unsigned char val = 0; | ||
123 | |||
124 | switch (irq) { | ||
125 | case 5: | ||
126 | val = 0x28; | ||
127 | break; | ||
128 | case 7: | ||
129 | val = 0x8; | ||
130 | break; | ||
131 | case 9: | ||
132 | val = 0x10; | ||
133 | break; | ||
134 | case 10: | ||
135 | val = 0x18; | ||
136 | break; | ||
137 | case 11: | ||
138 | val = 0x20; | ||
139 | break; | ||
140 | default: | ||
141 | break; | ||
142 | } | ||
143 | return val; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * sc6000_dma_to_softcfg - Decode dma number into cfg code. | ||
148 | */ | ||
149 | static __devinit unsigned char sc6000_dma_to_softcfg(int dma) | ||
150 | { | ||
151 | unsigned char val = 0; | ||
152 | |||
153 | switch (dma) { | ||
154 | case 0: | ||
155 | val = 1; | ||
156 | break; | ||
157 | case 1: | ||
158 | val = 2; | ||
159 | break; | ||
160 | case 3: | ||
161 | val = 3; | ||
162 | break; | ||
163 | default: | ||
164 | break; | ||
165 | } | ||
166 | return val; | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code. | ||
171 | */ | ||
172 | static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq) | ||
173 | { | ||
174 | unsigned char val = 0; | ||
175 | |||
176 | switch (mpu_irq) { | ||
177 | case 5: | ||
178 | val = 4; | ||
179 | break; | ||
180 | case 7: | ||
181 | val = 0x44; | ||
182 | break; | ||
183 | case 9: | ||
184 | val = 0x84; | ||
185 | break; | ||
186 | case 10: | ||
187 | val = 0xc4; | ||
188 | break; | ||
189 | default: | ||
190 | break; | ||
191 | } | ||
192 | return val; | ||
193 | } | ||
194 | |||
195 | static __devinit int sc6000_wait_data(char __iomem *vport) | ||
196 | { | ||
197 | int loop = 1000; | ||
198 | unsigned char val = 0; | ||
199 | |||
200 | do { | ||
201 | val = ioread8(vport + DSP_DATAVAIL); | ||
202 | if (val & 0x80) | ||
203 | return 0; | ||
204 | cpu_relax(); | ||
205 | } while (loop--); | ||
206 | |||
207 | return -EAGAIN; | ||
208 | } | ||
209 | |||
210 | static __devinit int sc6000_read(char __iomem *vport) | ||
211 | { | ||
212 | if (sc6000_wait_data(vport)) | ||
213 | return -EBUSY; | ||
214 | |||
215 | return ioread8(vport + DSP_READ); | ||
216 | |||
217 | } | ||
218 | |||
219 | static __devinit int sc6000_write(char __iomem *vport, int cmd) | ||
220 | { | ||
221 | unsigned char val; | ||
222 | int loop = 500000; | ||
223 | |||
224 | do { | ||
225 | val = ioread8(vport + DSP_STATUS); | ||
226 | /* | ||
227 | * DSP ready to receive data if bit 7 of val == 0 | ||
228 | */ | ||
229 | if (!(val & 0x80)) { | ||
230 | iowrite8(cmd, vport + DSP_COMMAND); | ||
231 | return 0; | ||
232 | } | ||
233 | cpu_relax(); | ||
234 | } while (loop--); | ||
235 | |||
236 | snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd); | ||
237 | |||
238 | return -EIO; | ||
239 | } | ||
240 | |||
241 | static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command, | ||
242 | char *data, int data_len) | ||
243 | { | ||
244 | int len = 0; | ||
245 | |||
246 | if (sc6000_write(vport, command)) { | ||
247 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command); | ||
248 | return -EIO; | ||
249 | } | ||
250 | |||
251 | do { | ||
252 | int val = sc6000_read(vport); | ||
253 | |||
254 | if (val < 0) | ||
255 | break; | ||
256 | |||
257 | data[len++] = val; | ||
258 | |||
259 | } while (len < data_len); | ||
260 | |||
261 | /* | ||
262 | * If no more data available, return to the caller, no error if len>0. | ||
263 | * We have no other way to know when the string is finished. | ||
264 | */ | ||
265 | return len ? len : -EIO; | ||
266 | } | ||
267 | |||
268 | static int __devinit sc6000_dsp_reset(char __iomem *vport) | ||
269 | { | ||
270 | iowrite8(1, vport + DSP_RESET); | ||
271 | udelay(10); | ||
272 | iowrite8(0, vport + DSP_RESET); | ||
273 | udelay(20); | ||
274 | if (sc6000_read(vport) == 0xaa) | ||
275 | return 0; | ||
276 | return -ENODEV; | ||
277 | } | ||
278 | |||
279 | /* detection and initialization */ | ||
280 | static int __devinit sc6000_cfg_write(char __iomem *vport, | ||
281 | unsigned char softcfg) | ||
282 | { | ||
283 | |||
284 | if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { | ||
285 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); | ||
286 | return -EIO; | ||
287 | } | ||
288 | if (sc6000_write(vport, softcfg)) { | ||
289 | snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); | ||
290 | return -EIO; | ||
291 | } | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int __devinit sc6000_setup_board(char __iomem *vport, int config) | ||
296 | { | ||
297 | int loop = 10; | ||
298 | |||
299 | do { | ||
300 | if (sc6000_write(vport, COMMAND_88)) { | ||
301 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", | ||
302 | COMMAND_88); | ||
303 | return -EIO; | ||
304 | } | ||
305 | } while ((sc6000_wait_data(vport) < 0) && loop--); | ||
306 | |||
307 | if (sc6000_read(vport) < 0) { | ||
308 | snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n", | ||
309 | COMMAND_88); | ||
310 | return -EIO; | ||
311 | } | ||
312 | |||
313 | if (sc6000_cfg_write(vport, config)) | ||
314 | return -ENODEV; | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static int __devinit sc6000_init_mss(char __iomem *vport, int config, | ||
320 | char __iomem *vmss_port, int mss_config) | ||
321 | { | ||
322 | if (sc6000_write(vport, DSP_INIT_MSS)) { | ||
323 | snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n", | ||
324 | DSP_INIT_MSS); | ||
325 | return -EIO; | ||
326 | } | ||
327 | |||
328 | msleep(10); | ||
329 | |||
330 | if (sc6000_cfg_write(vport, config)) | ||
331 | return -EIO; | ||
332 | |||
333 | iowrite8(mss_config, vmss_port); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | ||
339 | char __iomem *vmss_port, int mpu_irq) | ||
340 | { | ||
341 | char answer[15]; | ||
342 | char version[2]; | ||
343 | int mss_config = sc6000_irq_to_softcfg(irq) | | ||
344 | sc6000_dma_to_softcfg(dma); | ||
345 | int config = mss_config | | ||
346 | sc6000_mpu_irq_to_softcfg(mpu_irq); | ||
347 | int err; | ||
348 | |||
349 | err = sc6000_dsp_reset(vport); | ||
350 | if (err < 0) { | ||
351 | snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n"); | ||
352 | return err; | ||
353 | } | ||
354 | |||
355 | memset(answer, 0, sizeof(answer)); | ||
356 | err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15); | ||
357 | if (err <= 0) { | ||
358 | snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n"); | ||
359 | return -ENODEV; | ||
360 | } | ||
361 | /* | ||
362 | * My SC-6000 card return "SC-6000" in DSPCopyright, so | ||
363 | * if we have something different, we have to be warned. | ||
364 | * Mine returns "SC-6000A " - KH | ||
365 | */ | ||
366 | if (strncmp("SC-6000", answer, 7)) | ||
367 | snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); | ||
368 | |||
369 | if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) { | ||
370 | snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n"); | ||
371 | return -ENODEV; | ||
372 | } | ||
373 | printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n", | ||
374 | answer, version[0], version[1]); | ||
375 | |||
376 | /* | ||
377 | * 0x0A == (IRQ 7, DMA 1, MIRQ 0) | ||
378 | */ | ||
379 | err = sc6000_cfg_write(vport, 0x0a); | ||
380 | if (err < 0) { | ||
381 | snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); | ||
382 | return -EFAULT; | ||
383 | } | ||
384 | |||
385 | err = sc6000_setup_board(vport, config); | ||
386 | if (err < 0) { | ||
387 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); | ||
388 | return -ENODEV; | ||
389 | } | ||
390 | |||
391 | err = sc6000_init_mss(vport, config, vmss_port, mss_config); | ||
392 | if (err < 0) { | ||
393 | snd_printk(KERN_ERR "Can not initialize" | ||
394 | "Microsoft Sound System mode.\n"); | ||
395 | return -ENODEV; | ||
396 | } | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int __devinit snd_sc6000_mixer(struct snd_ad1848 *chip) | ||
402 | { | ||
403 | struct snd_card *card = chip->card; | ||
404 | struct snd_ctl_elem_id id1, id2; | ||
405 | int err; | ||
406 | |||
407 | memset(&id1, 0, sizeof(id1)); | ||
408 | memset(&id2, 0, sizeof(id2)); | ||
409 | id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
410 | id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
411 | /* reassign AUX0 to FM */ | ||
412 | strcpy(id1.name, "Aux Playback Switch"); | ||
413 | strcpy(id2.name, "FM Playback Switch"); | ||
414 | err = snd_ctl_rename_id(card, &id1, &id2); | ||
415 | if (err < 0) | ||
416 | return err; | ||
417 | strcpy(id1.name, "Aux Playback Volume"); | ||
418 | strcpy(id2.name, "FM Playback Volume"); | ||
419 | err = snd_ctl_rename_id(card, &id1, &id2); | ||
420 | if (err < 0) | ||
421 | return err; | ||
422 | /* reassign AUX1 to CD */ | ||
423 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
424 | strcpy(id2.name, "CD Playback Switch"); | ||
425 | err = snd_ctl_rename_id(card, &id1, &id2); | ||
426 | if (err < 0) | ||
427 | return err; | ||
428 | strcpy(id1.name, "Aux Playback Volume"); | ||
429 | strcpy(id2.name, "CD Playback Volume"); | ||
430 | err = snd_ctl_rename_id(card, &id1, &id2); | ||
431 | if (err < 0) | ||
432 | return err; | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static int __devinit snd_sc6000_match(struct device *devptr, unsigned int dev) | ||
437 | { | ||
438 | if (!enable[dev]) | ||
439 | return 0; | ||
440 | if (port[dev] == SNDRV_AUTO_PORT) { | ||
441 | printk(KERN_ERR PFX "specify IO port\n"); | ||
442 | return 0; | ||
443 | } | ||
444 | if (mss_port[dev] == SNDRV_AUTO_PORT) { | ||
445 | printk(KERN_ERR PFX "specify MSS port\n"); | ||
446 | return 0; | ||
447 | } | ||
448 | if (port[dev] != 0x220 && port[dev] != 0x240) { | ||
449 | printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n"); | ||
450 | return 0; | ||
451 | } | ||
452 | if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) { | ||
453 | printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n"); | ||
454 | return 0; | ||
455 | } | ||
456 | if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) { | ||
457 | printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]); | ||
458 | return 0; | ||
459 | } | ||
460 | if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) { | ||
461 | printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]); | ||
462 | return 0; | ||
463 | } | ||
464 | if (mpu_port[dev] != SNDRV_AUTO_PORT && | ||
465 | (mpu_port[dev] & ~0x30L) != 0x300) { | ||
466 | printk(KERN_ERR PFX "invalid MPU-401 port %lx\n", | ||
467 | mpu_port[dev]); | ||
468 | return 0; | ||
469 | } | ||
470 | if (mpu_port[dev] != SNDRV_AUTO_PORT && | ||
471 | mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 && | ||
472 | !sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) { | ||
473 | printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]); | ||
474 | return 0; | ||
475 | } | ||
476 | return 1; | ||
477 | } | ||
478 | |||
479 | static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | ||
480 | { | ||
481 | static int possible_irqs[] = { 5, 7, 9, 10, 11, -1 }; | ||
482 | static int possible_dmas[] = { 1, 3, 0, -1 }; | ||
483 | int err; | ||
484 | int xirq = irq[dev]; | ||
485 | int xdma = dma[dev]; | ||
486 | struct snd_card *card; | ||
487 | struct snd_ad1848 *chip; | ||
488 | struct snd_opl3 *opl3; | ||
489 | char __iomem *vport; | ||
490 | char __iomem *vmss_port; | ||
491 | |||
492 | |||
493 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
494 | if (!card) | ||
495 | return -ENOMEM; | ||
496 | |||
497 | if (xirq == SNDRV_AUTO_IRQ) { | ||
498 | xirq = snd_legacy_find_free_irq(possible_irqs); | ||
499 | if (xirq < 0) { | ||
500 | snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); | ||
501 | err = -EBUSY; | ||
502 | goto err_exit; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | if (xdma == SNDRV_AUTO_DMA) { | ||
507 | xdma = snd_legacy_find_free_dma(possible_dmas); | ||
508 | if (xdma < 0) { | ||
509 | snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); | ||
510 | err = -EBUSY; | ||
511 | goto err_exit; | ||
512 | } | ||
513 | } | ||
514 | |||
515 | if (!request_region(port[dev], 0x10, DRV_NAME)) { | ||
516 | snd_printk(KERN_ERR PFX | ||
517 | "I/O port region is already in use.\n"); | ||
518 | err = -EBUSY; | ||
519 | goto err_exit; | ||
520 | } | ||
521 | vport = devm_ioport_map(devptr, port[dev], 0x10); | ||
522 | if (!vport) { | ||
523 | snd_printk(KERN_ERR PFX | ||
524 | "I/O port cannot be iomaped.\n"); | ||
525 | err = -EBUSY; | ||
526 | goto err_unmap1; | ||
527 | } | ||
528 | |||
529 | /* to make it marked as used */ | ||
530 | if (!request_region(mss_port[dev], 4, DRV_NAME)) { | ||
531 | snd_printk(KERN_ERR PFX | ||
532 | "SC-6000 port I/O port region is already in use.\n"); | ||
533 | err = -EBUSY; | ||
534 | goto err_unmap1; | ||
535 | } | ||
536 | vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); | ||
537 | if (!vport) { | ||
538 | snd_printk(KERN_ERR PFX | ||
539 | "MSS port I/O cannot be iomaped.\n"); | ||
540 | err = -EBUSY; | ||
541 | goto err_unmap2; | ||
542 | } | ||
543 | |||
544 | snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n", | ||
545 | port[dev], xirq, xdma, | ||
546 | mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); | ||
547 | |||
548 | err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]); | ||
549 | if (err < 0) | ||
550 | goto err_unmap2; | ||
551 | |||
552 | err = snd_ad1848_create(card, mss_port[dev] + 4, xirq, xdma, | ||
553 | AD1848_HW_DETECT, &chip); | ||
554 | if (err < 0) | ||
555 | goto err_unmap2; | ||
556 | card->private_data = chip; | ||
557 | |||
558 | err = snd_ad1848_pcm(chip, 0, NULL); | ||
559 | if (err < 0) { | ||
560 | snd_printk(KERN_ERR PFX | ||
561 | "error creating new ad1848 PCM device\n"); | ||
562 | goto err_unmap2; | ||
563 | } | ||
564 | err = snd_ad1848_mixer(chip); | ||
565 | if (err < 0) { | ||
566 | snd_printk(KERN_ERR PFX "error creating new ad1848 mixer\n"); | ||
567 | goto err_unmap2; | ||
568 | } | ||
569 | err = snd_sc6000_mixer(chip); | ||
570 | if (err < 0) { | ||
571 | snd_printk(KERN_ERR PFX "the mixer rewrite failed\n"); | ||
572 | goto err_unmap2; | ||
573 | } | ||
574 | if (snd_opl3_create(card, | ||
575 | 0x388, 0x388 + 2, | ||
576 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
577 | snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n", | ||
578 | 0x388, 0x388 + 2); | ||
579 | } else { | ||
580 | err = snd_opl3_timer_new(opl3, 0, 1); | ||
581 | if (err < 0) | ||
582 | goto err_unmap2; | ||
583 | |||
584 | err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); | ||
585 | if (err < 0) | ||
586 | goto err_unmap2; | ||
587 | } | ||
588 | |||
589 | if (mpu_port[dev] != SNDRV_AUTO_PORT) { | ||
590 | if (mpu_irq[dev] == SNDRV_AUTO_IRQ) | ||
591 | mpu_irq[dev] = -1; | ||
592 | if (snd_mpu401_uart_new(card, 0, | ||
593 | MPU401_HW_MPU401, | ||
594 | mpu_port[dev], 0, | ||
595 | mpu_irq[dev], IRQF_DISABLED, | ||
596 | NULL) < 0) | ||
597 | snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n", | ||
598 | mpu_port[dev]); | ||
599 | } | ||
600 | |||
601 | strcpy(card->driver, DRV_NAME); | ||
602 | strcpy(card->shortname, "SC-6000"); | ||
603 | sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d", | ||
604 | mss_port[dev], xirq, xdma); | ||
605 | |||
606 | snd_card_set_dev(card, devptr); | ||
607 | |||
608 | err = snd_card_register(card); | ||
609 | if (err < 0) | ||
610 | goto err_unmap2; | ||
611 | |||
612 | dev_set_drvdata(devptr, card); | ||
613 | return 0; | ||
614 | |||
615 | err_unmap2: | ||
616 | release_region(mss_port[dev], 4); | ||
617 | err_unmap1: | ||
618 | release_region(port[dev], 0x10); | ||
619 | err_exit: | ||
620 | snd_card_free(card); | ||
621 | return err; | ||
622 | } | ||
623 | |||
624 | static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev) | ||
625 | { | ||
626 | release_region(port[dev], 0x10); | ||
627 | release_region(mss_port[dev], 4); | ||
628 | |||
629 | snd_card_free(dev_get_drvdata(devptr)); | ||
630 | dev_set_drvdata(devptr, NULL); | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static struct isa_driver snd_sc6000_driver = { | ||
635 | .match = snd_sc6000_match, | ||
636 | .probe = snd_sc6000_probe, | ||
637 | .remove = __devexit_p(snd_sc6000_remove), | ||
638 | /* FIXME: suspend/resume */ | ||
639 | .driver = { | ||
640 | .name = DRV_NAME, | ||
641 | }, | ||
642 | }; | ||
643 | |||
644 | |||
645 | static int __init alsa_card_sc6000_init(void) | ||
646 | { | ||
647 | return isa_register_driver(&snd_sc6000_driver, SNDRV_CARDS); | ||
648 | } | ||
649 | |||
650 | static void __exit alsa_card_sc6000_exit(void) | ||
651 | { | ||
652 | isa_unregister_driver(&snd_sc6000_driver); | ||
653 | } | ||
654 | |||
655 | module_init(alsa_card_sc6000_init) | ||
656 | module_exit(alsa_card_sc6000_exit) | ||