616 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Aztech AZT1605/AZT2316 Driver4 * Copyright (C) 2007,2010 Rene Herman5 */6 7#include <linux/kernel.h>8#include <linux/module.h>9#include <linux/isa.h>10#include <linux/delay.h>11#include <linux/io.h>12#include <asm/processor.h>13#include <sound/core.h>14#include <sound/initval.h>15#include <sound/wss.h>16#include <sound/mpu401.h>17#include <sound/opl3.h>18 19MODULE_DESCRIPTION(CRD_NAME);20MODULE_AUTHOR("Rene Herman");21MODULE_LICENSE("GPL");22 23static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;24static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;25static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;26 27module_param_array(index, int, NULL, 0444);28MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");29module_param_array(id, charp, NULL, 0444);30MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");31module_param_array(enable, bool, NULL, 0444);32MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");33 34static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;35static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;36static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;37static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;38static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;39static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;40static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;41static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;42 43module_param_hw_array(port, long, ioport, NULL, 0444);44MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");45module_param_hw_array(wss_port, long, ioport, NULL, 0444);46MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");47module_param_hw_array(mpu_port, long, ioport, NULL, 0444);48MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");49module_param_hw_array(fm_port, long, ioport, NULL, 0444);50MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");51module_param_hw_array(irq, int, irq, NULL, 0444);52MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");53module_param_hw_array(mpu_irq, int, irq, NULL, 0444);54MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");55module_param_hw_array(dma1, int, dma, NULL, 0444);56MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");57module_param_hw_array(dma2, int, dma, NULL, 0444);58MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");59 60/*61 * Generic SB DSP support routines62 */63 64#define DSP_PORT_RESET 0x665#define DSP_PORT_READ 0xa66#define DSP_PORT_COMMAND 0xc67#define DSP_PORT_STATUS 0xc68#define DSP_PORT_DATA_AVAIL 0xe69 70#define DSP_SIGNATURE 0xaa71 72#define DSP_COMMAND_GET_VERSION 0xe173 74static int dsp_get_byte(void __iomem *port, u8 *val)75{76 int loops = 1000;77 78 while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {79 if (!loops--)80 return -EIO;81 cpu_relax();82 }83 *val = ioread8(port + DSP_PORT_READ);84 return 0;85}86 87static int dsp_reset(void __iomem *port)88{89 u8 val;90 91 iowrite8(1, port + DSP_PORT_RESET);92 udelay(10);93 iowrite8(0, port + DSP_PORT_RESET);94 95 if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)96 return -ENODEV;97 98 return 0;99}100 101static int dsp_command(void __iomem *port, u8 cmd)102{103 int loops = 1000;104 105 while (ioread8(port + DSP_PORT_STATUS) & 0x80) {106 if (!loops--)107 return -EIO;108 cpu_relax();109 }110 iowrite8(cmd, port + DSP_PORT_COMMAND);111 return 0;112}113 114static int dsp_get_version(void __iomem *port, u8 *major, u8 *minor)115{116 int err;117 118 err = dsp_command(port, DSP_COMMAND_GET_VERSION);119 if (err < 0)120 return err;121 122 err = dsp_get_byte(port, major);123 if (err < 0)124 return err;125 126 err = dsp_get_byte(port, minor);127 if (err < 0)128 return err;129 130 return 0;131}132 133/*134 * Generic WSS support routines135 */136 137#define WSS_CONFIG_DMA_0 (1 << 0)138#define WSS_CONFIG_DMA_1 (2 << 0)139#define WSS_CONFIG_DMA_3 (3 << 0)140#define WSS_CONFIG_DUPLEX (1 << 2)141#define WSS_CONFIG_IRQ_7 (1 << 3)142#define WSS_CONFIG_IRQ_9 (2 << 3)143#define WSS_CONFIG_IRQ_10 (3 << 3)144#define WSS_CONFIG_IRQ_11 (4 << 3)145 146#define WSS_PORT_CONFIG 0147#define WSS_PORT_SIGNATURE 3148 149#define WSS_SIGNATURE 4150 151static int wss_detect(void __iomem *wss_port)152{153 if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)154 return -ENODEV;155 156 return 0;157}158 159static void wss_set_config(void __iomem *wss_port, u8 wss_config)160{161 iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);162}163 164/*165 * Aztech Sound Galaxy specifics166 */167 168#define GALAXY_PORT_CONFIG 1024169#define CONFIG_PORT_SET 4170 171#define DSP_COMMAND_GALAXY_8 8172#define GALAXY_COMMAND_GET_TYPE 5173 174#define DSP_COMMAND_GALAXY_9 9175#define GALAXY_COMMAND_WSSMODE 0176#define GALAXY_COMMAND_SB8MODE 1177 178#define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE179#define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE180 181struct snd_galaxy {182 void __iomem *port;183 void __iomem *config_port;184 void __iomem *wss_port;185 u32 config;186 struct resource *res_port;187 struct resource *res_config_port;188 struct resource *res_wss_port;189};190 191static u32 config[SNDRV_CARDS];192static u8 wss_config[SNDRV_CARDS];193 194static int snd_galaxy_match(struct device *dev, unsigned int n)195{196 if (!enable[n])197 return 0;198 199 switch (port[n]) {200 case SNDRV_AUTO_PORT:201 dev_err(dev, "please specify port\n");202 return 0;203 case 0x220:204 config[n] |= GALAXY_CONFIG_SBA_220;205 break;206 case 0x240:207 config[n] |= GALAXY_CONFIG_SBA_240;208 break;209 case 0x260:210 config[n] |= GALAXY_CONFIG_SBA_260;211 break;212 case 0x280:213 config[n] |= GALAXY_CONFIG_SBA_280;214 break;215 default:216 dev_err(dev, "invalid port %#lx\n", port[n]);217 return 0;218 }219 220 switch (wss_port[n]) {221 case SNDRV_AUTO_PORT:222 dev_err(dev, "please specify wss_port\n");223 return 0;224 case 0x530:225 config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;226 break;227 case 0x604:228 config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;229 break;230 case 0xe80:231 config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;232 break;233 case 0xf40:234 config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;235 break;236 default:237 dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);238 return 0;239 }240 241 switch (irq[n]) {242 case SNDRV_AUTO_IRQ:243 dev_err(dev, "please specify irq\n");244 return 0;245 case 7:246 wss_config[n] |= WSS_CONFIG_IRQ_7;247 break;248 case 2:249 irq[n] = 9;250 fallthrough;251 case 9:252 wss_config[n] |= WSS_CONFIG_IRQ_9;253 break;254 case 10:255 wss_config[n] |= WSS_CONFIG_IRQ_10;256 break;257 case 11:258 wss_config[n] |= WSS_CONFIG_IRQ_11;259 break;260 default:261 dev_err(dev, "invalid IRQ %d\n", irq[n]);262 return 0;263 }264 265 switch (dma1[n]) {266 case SNDRV_AUTO_DMA:267 dev_err(dev, "please specify dma1\n");268 return 0;269 case 0:270 wss_config[n] |= WSS_CONFIG_DMA_0;271 break;272 case 1:273 wss_config[n] |= WSS_CONFIG_DMA_1;274 break;275 case 3:276 wss_config[n] |= WSS_CONFIG_DMA_3;277 break;278 default:279 dev_err(dev, "invalid playback DMA %d\n", dma1[n]);280 return 0;281 }282 283 if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {284 dma2[n] = -1;285 goto mpu;286 }287 288 wss_config[n] |= WSS_CONFIG_DUPLEX;289 switch (dma2[n]) {290 case 0:291 break;292 case 1:293 if (dma1[n] == 0)294 break;295 fallthrough;296 default:297 dev_err(dev, "invalid capture DMA %d\n", dma2[n]);298 return 0;299 }300 301mpu:302 switch (mpu_port[n]) {303 case SNDRV_AUTO_PORT:304 dev_warn(dev, "mpu_port not specified; not using MPU-401\n");305 mpu_port[n] = -1;306 goto fm;307 case 0x300:308 config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;309 break;310 case 0x330:311 config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;312 break;313 default:314 dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);315 return 0;316 }317 318 switch (mpu_irq[n]) {319 case SNDRV_AUTO_IRQ:320 dev_warn(dev, "mpu_irq not specified: using polling mode\n");321 mpu_irq[n] = -1;322 break;323 case 2:324 mpu_irq[n] = 9;325 fallthrough;326 case 9:327 config[n] |= GALAXY_CONFIG_MPUIRQ_2;328 break;329#ifdef AZT1605330 case 3:331 config[n] |= GALAXY_CONFIG_MPUIRQ_3;332 break;333#endif334 case 5:335 config[n] |= GALAXY_CONFIG_MPUIRQ_5;336 break;337 case 7:338 config[n] |= GALAXY_CONFIG_MPUIRQ_7;339 break;340#ifdef AZT2316341 case 10:342 config[n] |= GALAXY_CONFIG_MPUIRQ_10;343 break;344#endif345 default:346 dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);347 return 0;348 }349 350 if (mpu_irq[n] == irq[n]) {351 dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");352 return 0;353 }354 355fm:356 switch (fm_port[n]) {357 case SNDRV_AUTO_PORT:358 dev_warn(dev, "fm_port not specified: not using OPL3\n");359 fm_port[n] = -1;360 break;361 case 0x388:362 break;363 default:364 dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);365 return 0;366 }367 368 config[n] |= GALAXY_CONFIG_GAME_ENABLE;369 return 1;370}371 372static int galaxy_init(struct snd_galaxy *galaxy, u8 *type)373{374 u8 major;375 u8 minor;376 int err;377 378 err = dsp_reset(galaxy->port);379 if (err < 0)380 return err;381 382 err = dsp_get_version(galaxy->port, &major, &minor);383 if (err < 0)384 return err;385 386 if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)387 return -ENODEV;388 389 err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);390 if (err < 0)391 return err;392 393 err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);394 if (err < 0)395 return err;396 397 err = dsp_get_byte(galaxy->port, type);398 if (err < 0)399 return err;400 401 return 0;402}403 404static int galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)405{406 int err;407 408 err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);409 if (err < 0)410 return err;411 412 err = dsp_command(galaxy->port, mode);413 if (err < 0)414 return err;415 416#ifdef AZT1605417 /*418 * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again419 */420 err = dsp_reset(galaxy->port);421 if (err < 0)422 return err;423#endif424 425 return 0;426}427 428static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)429{430 u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);431 int i;432 433 iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);434 for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {435 iowrite8(config, galaxy->config_port + i);436 config >>= 8;437 }438 iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);439 msleep(10);440}441 442static void galaxy_config(struct snd_galaxy *galaxy, u32 config)443{444 int i;445 446 for (i = GALAXY_CONFIG_SIZE; i; i--) {447 u8 tmp = ioread8(galaxy->config_port + i - 1);448 galaxy->config = (galaxy->config << 8) | tmp;449 }450 config |= galaxy->config & GALAXY_CONFIG_MASK;451 galaxy_set_config(galaxy, config);452}453 454static int galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)455{456 int err;457 458 err = wss_detect(galaxy->wss_port);459 if (err < 0)460 return err;461 462 wss_set_config(galaxy->wss_port, wss_config);463 464 err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);465 if (err < 0)466 return err;467 468 return 0;469}470 471static void snd_galaxy_free(struct snd_card *card)472{473 struct snd_galaxy *galaxy = card->private_data;474 475 if (galaxy->wss_port)476 wss_set_config(galaxy->wss_port, 0);477 if (galaxy->config_port)478 galaxy_set_config(galaxy, galaxy->config);479}480 481static int __snd_galaxy_probe(struct device *dev, unsigned int n)482{483 struct snd_galaxy *galaxy;484 struct snd_wss *chip;485 struct snd_card *card;486 u8 type;487 int err;488 489 err = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,490 sizeof(*galaxy), &card);491 if (err < 0)492 return err;493 494 card->private_free = snd_galaxy_free;495 galaxy = card->private_data;496 497 galaxy->res_port = devm_request_region(dev, port[n], 16, DRV_NAME);498 if (!galaxy->res_port) {499 dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],500 port[n] + 15);501 return -EBUSY;502 }503 galaxy->port = devm_ioport_map(dev, port[n], 16);504 if (!galaxy->port)505 return -ENOMEM;506 507 err = galaxy_init(galaxy, &type);508 if (err < 0) {509 dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);510 return err;511 }512 dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);513 514 galaxy->res_config_port =515 devm_request_region(dev, port[n] + GALAXY_PORT_CONFIG, 16,516 DRV_NAME);517 if (!galaxy->res_config_port) {518 dev_err(dev, "could not grab ports %#lx-%#lx\n",519 port[n] + GALAXY_PORT_CONFIG,520 port[n] + GALAXY_PORT_CONFIG + 15);521 return -EBUSY;522 }523 galaxy->config_port =524 devm_ioport_map(dev, port[n] + GALAXY_PORT_CONFIG, 16);525 if (!galaxy->config_port)526 return -ENOMEM;527 galaxy_config(galaxy, config[n]);528 529 galaxy->res_wss_port = devm_request_region(dev, wss_port[n], 4, DRV_NAME);530 if (!galaxy->res_wss_port) {531 dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],532 wss_port[n] + 3);533 return -EBUSY;534 }535 galaxy->wss_port = devm_ioport_map(dev, wss_port[n], 4);536 if (!galaxy->wss_port)537 return -ENOMEM;538 539 err = galaxy_wss_config(galaxy, wss_config[n]);540 if (err < 0) {541 dev_err(dev, "could not configure WSS\n");542 return err;543 }544 545 strcpy(card->driver, DRV_NAME);546 strcpy(card->shortname, DRV_NAME);547 sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",548 card->shortname, port[n], wss_port[n], irq[n], dma1[n],549 dma2[n]);550 551 err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],552 dma2[n], WSS_HW_DETECT, 0, &chip);553 if (err < 0)554 return err;555 556 err = snd_wss_pcm(chip, 0);557 if (err < 0)558 return err;559 560 err = snd_wss_mixer(chip);561 if (err < 0)562 return err;563 564 err = snd_wss_timer(chip, 0);565 if (err < 0)566 return err;567 568 if (mpu_port[n] >= 0) {569 err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,570 mpu_port[n], 0, mpu_irq[n], NULL);571 if (err < 0)572 return err;573 }574 575 if (fm_port[n] >= 0) {576 struct snd_opl3 *opl3;577 578 err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,579 OPL3_HW_AUTO, 0, &opl3);580 if (err < 0) {581 dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);582 return err;583 }584 err = snd_opl3_timer_new(opl3, 1, 2);585 if (err < 0)586 return err;587 588 err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);589 if (err < 0)590 return err;591 }592 593 err = snd_card_register(card);594 if (err < 0)595 return err;596 597 dev_set_drvdata(dev, card);598 return 0;599}600 601static int snd_galaxy_probe(struct device *dev, unsigned int n)602{603 return snd_card_free_on_error(dev, __snd_galaxy_probe(dev, n));604}605 606static struct isa_driver snd_galaxy_driver = {607 .match = snd_galaxy_match,608 .probe = snd_galaxy_probe,609 610 .driver = {611 .name = DEV_NAME612 }613};614 615module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);616