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