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