0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/module.h>
0016 #include <linux/delay.h>
0017 #include <linux/isa.h>
0018 #include <linux/io.h>
0019 #include <asm/dma.h>
0020 #include <sound/core.h>
0021 #include <sound/wss.h>
0022 #include <sound/opl3.h>
0023 #include <sound/mpu401.h>
0024 #include <sound/control.h>
0025 #define SNDRV_LEGACY_FIND_FREE_IRQ
0026 #define SNDRV_LEGACY_FIND_FREE_DMA
0027 #include <sound/initval.h>
0028
0029 MODULE_AUTHOR("Krzysztof Helt");
0030 MODULE_DESCRIPTION("Gallant SC-6000");
0031 MODULE_LICENSE("GPL");
0032
0033 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
0034 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
0035 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
0036 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
0037 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
0038 static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
0039 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
0040
0041 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
0042 static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
0043 static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false };
0044
0045 module_param_array(index, int, NULL, 0444);
0046 MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");
0047 module_param_array(id, charp, NULL, 0444);
0048 MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");
0049 module_param_array(enable, bool, NULL, 0444);
0050 MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");
0051 module_param_hw_array(port, long, ioport, NULL, 0444);
0052 MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");
0053 module_param_hw_array(mss_port, long, ioport, NULL, 0444);
0054 MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");
0055 module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
0056 MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");
0057 module_param_hw_array(irq, int, irq, NULL, 0444);
0058 MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");
0059 module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
0060 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
0061 module_param_hw_array(dma, int, dma, NULL, 0444);
0062 MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
0063 module_param_array(joystick, bool, NULL, 0444);
0064 MODULE_PARM_DESC(joystick, "Enable gameport.");
0065
0066
0067
0068
0069
0070 #define WRITE_MDIRQ_CFG 0x50
0071 #define COMMAND_52 0x52
0072 #define READ_HARD_CFG 0x58
0073 #define COMMAND_5C 0x5c
0074 #define COMMAND_60 0x60
0075 #define COMMAND_66 0x66
0076 #define COMMAND_6C 0x6c
0077 #define COMMAND_6E 0x6e
0078 #define COMMAND_88 0x88
0079 #define DSP_INIT_MSS 0x8c
0080 #define COMMAND_C5 0xc5
0081 #define GET_DSP_VERSION 0xe1
0082 #define GET_DSP_COPYRIGHT 0xe3
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093 #define DSP_RESET 0x06
0094 #define DSP_READ 0x0a
0095 #define DSP_WRITE 0x0c
0096 #define DSP_COMMAND 0x0c
0097 #define DSP_STATUS 0x0c
0098 #define DSP_DATAVAIL 0x0e
0099
0100 #define PFX "sc6000: "
0101 #define DRV_NAME "SC-6000"
0102
0103
0104
0105
0106
0107
0108 static unsigned char sc6000_irq_to_softcfg(int irq)
0109 {
0110 unsigned char val = 0;
0111
0112 switch (irq) {
0113 case 5:
0114 val = 0x28;
0115 break;
0116 case 7:
0117 val = 0x8;
0118 break;
0119 case 9:
0120 val = 0x10;
0121 break;
0122 case 10:
0123 val = 0x18;
0124 break;
0125 case 11:
0126 val = 0x20;
0127 break;
0128 default:
0129 break;
0130 }
0131 return val;
0132 }
0133
0134
0135
0136
0137 static unsigned char sc6000_dma_to_softcfg(int dma)
0138 {
0139 unsigned char val = 0;
0140
0141 switch (dma) {
0142 case 0:
0143 val = 1;
0144 break;
0145 case 1:
0146 val = 2;
0147 break;
0148 case 3:
0149 val = 3;
0150 break;
0151 default:
0152 break;
0153 }
0154 return val;
0155 }
0156
0157
0158
0159
0160 static unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
0161 {
0162 unsigned char val = 0;
0163
0164 switch (mpu_irq) {
0165 case 5:
0166 val = 4;
0167 break;
0168 case 7:
0169 val = 0x44;
0170 break;
0171 case 9:
0172 val = 0x84;
0173 break;
0174 case 10:
0175 val = 0xc4;
0176 break;
0177 default:
0178 break;
0179 }
0180 return val;
0181 }
0182
0183 static int sc6000_wait_data(char __iomem *vport)
0184 {
0185 int loop = 1000;
0186 unsigned char val = 0;
0187
0188 do {
0189 val = ioread8(vport + DSP_DATAVAIL);
0190 if (val & 0x80)
0191 return 0;
0192 cpu_relax();
0193 } while (loop--);
0194
0195 return -EAGAIN;
0196 }
0197
0198 static int sc6000_read(char __iomem *vport)
0199 {
0200 if (sc6000_wait_data(vport))
0201 return -EBUSY;
0202
0203 return ioread8(vport + DSP_READ);
0204
0205 }
0206
0207 static int sc6000_write(char __iomem *vport, int cmd)
0208 {
0209 unsigned char val;
0210 int loop = 500000;
0211
0212 do {
0213 val = ioread8(vport + DSP_STATUS);
0214
0215
0216
0217 if (!(val & 0x80)) {
0218 iowrite8(cmd, vport + DSP_COMMAND);
0219 return 0;
0220 }
0221 cpu_relax();
0222 } while (loop--);
0223
0224 snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd);
0225
0226 return -EIO;
0227 }
0228
0229 static int sc6000_dsp_get_answer(char __iomem *vport, int command,
0230 char *data, int data_len)
0231 {
0232 int len = 0;
0233
0234 if (sc6000_write(vport, command)) {
0235 snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command);
0236 return -EIO;
0237 }
0238
0239 do {
0240 int val = sc6000_read(vport);
0241
0242 if (val < 0)
0243 break;
0244
0245 data[len++] = val;
0246
0247 } while (len < data_len);
0248
0249
0250
0251
0252
0253 return len ? len : -EIO;
0254 }
0255
0256 static int sc6000_dsp_reset(char __iomem *vport)
0257 {
0258 iowrite8(1, vport + DSP_RESET);
0259 udelay(10);
0260 iowrite8(0, vport + DSP_RESET);
0261 udelay(20);
0262 if (sc6000_read(vport) == 0xaa)
0263 return 0;
0264 return -ENODEV;
0265 }
0266
0267
0268 static int sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
0269 {
0270 if (sc6000_write(vport, COMMAND_6C) < 0) {
0271 snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
0272 return -EIO;
0273 }
0274 if (sc6000_write(vport, COMMAND_5C) < 0) {
0275 snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
0276 return -EIO;
0277 }
0278 if (sc6000_write(vport, cfg[0]) < 0) {
0279 snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
0280 return -EIO;
0281 }
0282 if (sc6000_write(vport, cfg[1]) < 0) {
0283 snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
0284 return -EIO;
0285 }
0286 if (sc6000_write(vport, COMMAND_C5) < 0) {
0287 snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
0288 return -EIO;
0289 }
0290
0291 return 0;
0292 }
0293
0294 static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
0295 {
0296
0297 if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
0298 snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
0299 return -EIO;
0300 }
0301 if (sc6000_write(vport, softcfg)) {
0302 snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
0303 return -EIO;
0304 }
0305 return 0;
0306 }
0307
0308 static int sc6000_setup_board(char __iomem *vport, int config)
0309 {
0310 int loop = 10;
0311
0312 do {
0313 if (sc6000_write(vport, COMMAND_88)) {
0314 snd_printk(KERN_ERR "CMD 0x%x: failed!\n",
0315 COMMAND_88);
0316 return -EIO;
0317 }
0318 } while ((sc6000_wait_data(vport) < 0) && loop--);
0319
0320 if (sc6000_read(vport) < 0) {
0321 snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n",
0322 COMMAND_88);
0323 return -EIO;
0324 }
0325
0326 if (sc6000_cfg_write(vport, config))
0327 return -ENODEV;
0328
0329 return 0;
0330 }
0331
0332 static int sc6000_init_mss(char __iomem *vport, int config,
0333 char __iomem *vmss_port, int mss_config)
0334 {
0335 if (sc6000_write(vport, DSP_INIT_MSS)) {
0336 snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n",
0337 DSP_INIT_MSS);
0338 return -EIO;
0339 }
0340
0341 msleep(10);
0342
0343 if (sc6000_cfg_write(vport, config))
0344 return -EIO;
0345
0346 iowrite8(mss_config, vmss_port);
0347
0348 return 0;
0349 }
0350
0351 static void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
0352 long xport, long xmpu,
0353 long xmss_port, int joystick)
0354 {
0355 cfg[0] = 0;
0356 cfg[1] = 0;
0357 if (xport == 0x240)
0358 cfg[0] |= 1;
0359 if (xmpu != SNDRV_AUTO_PORT) {
0360 cfg[0] |= (xmpu & 0x30) >> 2;
0361 cfg[1] |= 0x20;
0362 }
0363 if (xmss_port == 0xe80)
0364 cfg[0] |= 0x10;
0365 cfg[0] |= 0x40;
0366 if (!joystick)
0367 cfg[0] |= 0x02;
0368 cfg[1] |= 0x80;
0369 cfg[1] &= ~0x40;
0370 snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
0371 }
0372
0373 static int sc6000_init_board(char __iomem *vport,
0374 char __iomem *vmss_port, int dev)
0375 {
0376 char answer[15];
0377 char version[2];
0378 int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
0379 sc6000_dma_to_softcfg(dma[dev]);
0380 int config = mss_config |
0381 sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
0382 int err;
0383 int old = 0;
0384
0385 err = sc6000_dsp_reset(vport);
0386 if (err < 0) {
0387 snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n");
0388 return err;
0389 }
0390
0391 memset(answer, 0, sizeof(answer));
0392 err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15);
0393 if (err <= 0) {
0394 snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n");
0395 return -ENODEV;
0396 }
0397
0398
0399
0400
0401 if (strncmp("SC-6000", answer, 7))
0402 snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
0403
0404 if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) {
0405 snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n");
0406 return -ENODEV;
0407 }
0408 printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
0409 answer, version[0], version[1]);
0410
0411
0412 sc6000_write(vport, COMMAND_5C);
0413 if (sc6000_read(vport) < 0)
0414 old = 1;
0415
0416 if (!old) {
0417 int cfg[2];
0418 sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
0419 mss_port[dev], joystick[dev]);
0420 if (sc6000_hw_cfg_write(vport, cfg) < 0) {
0421 snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
0422 return -EIO;
0423 }
0424 }
0425 err = sc6000_setup_board(vport, config);
0426 if (err < 0) {
0427 snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
0428 return -ENODEV;
0429 }
0430
0431 sc6000_dsp_reset(vport);
0432
0433 if (!old) {
0434 sc6000_write(vport, COMMAND_60);
0435 sc6000_write(vport, 0x02);
0436 sc6000_dsp_reset(vport);
0437 }
0438
0439 err = sc6000_setup_board(vport, config);
0440 if (err < 0) {
0441 snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
0442 return -ENODEV;
0443 }
0444 err = sc6000_init_mss(vport, config, vmss_port, mss_config);
0445 if (err < 0) {
0446 snd_printk(KERN_ERR "Cannot initialize "
0447 "Microsoft Sound System mode.\n");
0448 return -ENODEV;
0449 }
0450
0451 return 0;
0452 }
0453
0454 static int snd_sc6000_mixer(struct snd_wss *chip)
0455 {
0456 struct snd_card *card = chip->card;
0457 struct snd_ctl_elem_id id1, id2;
0458 int err;
0459
0460 memset(&id1, 0, sizeof(id1));
0461 memset(&id2, 0, sizeof(id2));
0462 id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0463 id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0464
0465 strcpy(id1.name, "Aux Playback Switch");
0466 strcpy(id2.name, "FM Playback Switch");
0467 err = snd_ctl_rename_id(card, &id1, &id2);
0468 if (err < 0)
0469 return err;
0470 strcpy(id1.name, "Aux Playback Volume");
0471 strcpy(id2.name, "FM Playback Volume");
0472 err = snd_ctl_rename_id(card, &id1, &id2);
0473 if (err < 0)
0474 return err;
0475
0476 strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
0477 strcpy(id2.name, "CD Playback Switch");
0478 err = snd_ctl_rename_id(card, &id1, &id2);
0479 if (err < 0)
0480 return err;
0481 strcpy(id1.name, "Aux Playback Volume");
0482 strcpy(id2.name, "CD Playback Volume");
0483 err = snd_ctl_rename_id(card, &id1, &id2);
0484 if (err < 0)
0485 return err;
0486 return 0;
0487 }
0488
0489 static int snd_sc6000_match(struct device *devptr, unsigned int dev)
0490 {
0491 if (!enable[dev])
0492 return 0;
0493 if (port[dev] == SNDRV_AUTO_PORT) {
0494 printk(KERN_ERR PFX "specify IO port\n");
0495 return 0;
0496 }
0497 if (mss_port[dev] == SNDRV_AUTO_PORT) {
0498 printk(KERN_ERR PFX "specify MSS port\n");
0499 return 0;
0500 }
0501 if (port[dev] != 0x220 && port[dev] != 0x240) {
0502 printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n");
0503 return 0;
0504 }
0505 if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) {
0506 printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n");
0507 return 0;
0508 }
0509 if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) {
0510 printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]);
0511 return 0;
0512 }
0513 if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) {
0514 printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]);
0515 return 0;
0516 }
0517 if (mpu_port[dev] != SNDRV_AUTO_PORT &&
0518 (mpu_port[dev] & ~0x30L) != 0x300) {
0519 printk(KERN_ERR PFX "invalid MPU-401 port %lx\n",
0520 mpu_port[dev]);
0521 return 0;
0522 }
0523 if (mpu_port[dev] != SNDRV_AUTO_PORT &&
0524 mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 &&
0525 !sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) {
0526 printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
0527 return 0;
0528 }
0529 return 1;
0530 }
0531
0532 static void snd_sc6000_free(struct snd_card *card)
0533 {
0534 char __iomem *vport = (char __force __iomem *)card->private_data;
0535
0536 if (vport)
0537 sc6000_setup_board(vport, 0);
0538 }
0539
0540 static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
0541 {
0542 static const int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
0543 static const int possible_dmas[] = { 1, 3, 0, -1 };
0544 int err;
0545 int xirq = irq[dev];
0546 int xdma = dma[dev];
0547 struct snd_card *card;
0548 struct snd_wss *chip;
0549 struct snd_opl3 *opl3;
0550 char __iomem *vport;
0551 char __iomem *vmss_port;
0552
0553 err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
0554 0, &card);
0555 if (err < 0)
0556 return err;
0557
0558 if (xirq == SNDRV_AUTO_IRQ) {
0559 xirq = snd_legacy_find_free_irq(possible_irqs);
0560 if (xirq < 0) {
0561 snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
0562 return -EBUSY;
0563 }
0564 }
0565
0566 if (xdma == SNDRV_AUTO_DMA) {
0567 xdma = snd_legacy_find_free_dma(possible_dmas);
0568 if (xdma < 0) {
0569 snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
0570 return -EBUSY;
0571 }
0572 }
0573
0574 if (!devm_request_region(devptr, port[dev], 0x10, DRV_NAME)) {
0575 snd_printk(KERN_ERR PFX
0576 "I/O port region is already in use.\n");
0577 return -EBUSY;
0578 }
0579 vport = devm_ioport_map(devptr, port[dev], 0x10);
0580 if (!vport) {
0581 snd_printk(KERN_ERR PFX
0582 "I/O port cannot be iomapped.\n");
0583 return -EBUSY;
0584 }
0585 card->private_data = (void __force *)vport;
0586
0587
0588 if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
0589 snd_printk(KERN_ERR PFX
0590 "SC-6000 port I/O port region is already in use.\n");
0591 return -EBUSY;
0592 }
0593 vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
0594 if (!vmss_port) {
0595 snd_printk(KERN_ERR PFX
0596 "MSS port I/O cannot be iomapped.\n");
0597 return -EBUSY;
0598 }
0599
0600 snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
0601 port[dev], xirq, xdma,
0602 mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
0603
0604 err = sc6000_init_board(vport, vmss_port, dev);
0605 if (err < 0)
0606 return err;
0607 card->private_free = snd_sc6000_free;
0608
0609 err = snd_wss_create(card, mss_port[dev] + 4, -1, xirq, xdma, -1,
0610 WSS_HW_DETECT, 0, &chip);
0611 if (err < 0)
0612 return err;
0613
0614 err = snd_wss_pcm(chip, 0);
0615 if (err < 0) {
0616 snd_printk(KERN_ERR PFX
0617 "error creating new WSS PCM device\n");
0618 return err;
0619 }
0620 err = snd_wss_mixer(chip);
0621 if (err < 0) {
0622 snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
0623 return err;
0624 }
0625 err = snd_sc6000_mixer(chip);
0626 if (err < 0) {
0627 snd_printk(KERN_ERR PFX "the mixer rewrite failed\n");
0628 return err;
0629 }
0630 if (snd_opl3_create(card,
0631 0x388, 0x388 + 2,
0632 OPL3_HW_AUTO, 0, &opl3) < 0) {
0633 snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n",
0634 0x388, 0x388 + 2);
0635 } else {
0636 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
0637 if (err < 0)
0638 return err;
0639 }
0640
0641 if (mpu_port[dev] != SNDRV_AUTO_PORT) {
0642 if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
0643 mpu_irq[dev] = -1;
0644 if (snd_mpu401_uart_new(card, 0,
0645 MPU401_HW_MPU401,
0646 mpu_port[dev], 0,
0647 mpu_irq[dev], NULL) < 0)
0648 snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
0649 mpu_port[dev]);
0650 }
0651
0652 strcpy(card->driver, DRV_NAME);
0653 strcpy(card->shortname, "SC-6000");
0654 sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d",
0655 mss_port[dev], xirq, xdma);
0656
0657 err = snd_card_register(card);
0658 if (err < 0)
0659 return err;
0660
0661 dev_set_drvdata(devptr, card);
0662 return 0;
0663 }
0664
0665 static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
0666 {
0667 return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev));
0668 }
0669
0670 static struct isa_driver snd_sc6000_driver = {
0671 .match = snd_sc6000_match,
0672 .probe = snd_sc6000_probe,
0673
0674 .driver = {
0675 .name = DRV_NAME,
0676 },
0677 };
0678
0679
0680 module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);