linux/sound/isa/galaxy/galaxy.c
<<
>>
Prefs
   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