linux/sound/isa/galaxy/galaxy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Aztech AZT1605/AZT2316 Driver
   4 * Copyright (C) 2007,2010  Rene Herman
   5 */
   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 routines
  62 */
  63
  64#define DSP_PORT_RESET          0x6
  65#define DSP_PORT_READ           0xa
  66#define DSP_PORT_COMMAND        0xc
  67#define DSP_PORT_STATUS         0xc
  68#define DSP_PORT_DATA_AVAIL     0xe
  69
  70#define DSP_SIGNATURE           0xaa
  71
  72#define DSP_COMMAND_GET_VERSION 0xe1
  73
  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 routines
 135 */
 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         0
 147#define WSS_PORT_SIGNATURE      3
 148
 149#define WSS_SIGNATURE           4
 150
 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 specifics
 166 */
 167
 168#define GALAXY_PORT_CONFIG      1024
 169#define CONFIG_PORT_SET         4
 170
 171#define DSP_COMMAND_GALAXY_8    8
 172#define GALAXY_COMMAND_GET_TYPE 5
 173
 174#define DSP_COMMAND_GALAXY_9    9
 175#define GALAXY_COMMAND_WSSMODE  0
 176#define GALAXY_COMMAND_SB8MODE  1
 177
 178#define GALAXY_MODE_WSS         GALAXY_COMMAND_WSSMODE
 179#define GALAXY_MODE_SB8         GALAXY_COMMAND_SB8MODE
 180
 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                /* Fall through */
 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                /* Fall through */
 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                /* Fall through */
 326        case 9:
 327                config[n] |= GALAXY_CONFIG_MPUIRQ_2;
 328                break;
 329#ifdef AZT1605
 330        case 3:
 331                config[n] |= GALAXY_CONFIG_MPUIRQ_3;
 332                break;
 333#endif
 334        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 AZT2316
 341        case 10:
 342                config[n] |= GALAXY_CONFIG_MPUIRQ_10;
 343                break;
 344#endif
 345        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 AZT1605
 417        /*
 418         * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
 419         */
 420        err = dsp_reset(galaxy->port);
 421        if (err < 0)
 422                return err;
 423#endif
 424
 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                ioport_unmap(galaxy->wss_port);
 478                release_and_free_resource(galaxy->res_wss_port);
 479        }
 480        if (galaxy->config_port) {
 481                galaxy_set_config(galaxy, galaxy->config);
 482                ioport_unmap(galaxy->config_port);
 483                release_and_free_resource(galaxy->res_config_port);
 484        }
 485        if (galaxy->port) {
 486                ioport_unmap(galaxy->port);
 487                release_and_free_resource(galaxy->res_port);
 488        }
 489}
 490
 491static int snd_galaxy_probe(struct device *dev, unsigned int n)
 492{
 493        struct snd_galaxy *galaxy;
 494        struct snd_wss *chip;
 495        struct snd_card *card;
 496        u8 type;
 497        int err;
 498
 499        err = snd_card_new(dev, index[n], id[n], THIS_MODULE,
 500                           sizeof(*galaxy), &card);
 501        if (err < 0)
 502                return err;
 503
 504        card->private_free = snd_galaxy_free;
 505        galaxy = card->private_data;
 506
 507        galaxy->res_port = request_region(port[n], 16, DRV_NAME);
 508        if (!galaxy->res_port) {
 509                dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
 510                        port[n] + 15);
 511                err = -EBUSY;
 512                goto error;
 513        }
 514        galaxy->port = ioport_map(port[n], 16);
 515
 516        err = galaxy_init(galaxy, &type);
 517        if (err < 0) {
 518                dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
 519                goto error;
 520        }
 521        dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
 522
 523        galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
 524                                                 16, DRV_NAME);
 525        if (!galaxy->res_config_port) {
 526                dev_err(dev, "could not grab ports %#lx-%#lx\n",
 527                        port[n] + GALAXY_PORT_CONFIG,
 528                        port[n] + GALAXY_PORT_CONFIG + 15);
 529                err = -EBUSY;
 530                goto error;
 531        }
 532        galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
 533
 534        galaxy_config(galaxy, config[n]);
 535
 536        galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
 537        if (!galaxy->res_wss_port)  {
 538                dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
 539                        wss_port[n] + 3);
 540                err = -EBUSY;
 541                goto error;
 542        }
 543        galaxy->wss_port = ioport_map(wss_port[n], 4);
 544
 545        err = galaxy_wss_config(galaxy, wss_config[n]);
 546        if (err < 0) {
 547                dev_err(dev, "could not configure WSS\n");
 548                goto error;
 549        }
 550
 551        strcpy(card->driver, DRV_NAME);
 552        strcpy(card->shortname, DRV_NAME);
 553        sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
 554                card->shortname, port[n], wss_port[n], irq[n], dma1[n],
 555                dma2[n]);
 556
 557        err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
 558                             dma2[n], WSS_HW_DETECT, 0, &chip);
 559        if (err < 0)
 560                goto error;
 561
 562        err = snd_wss_pcm(chip, 0);
 563        if (err < 0)
 564                goto error;
 565
 566        err = snd_wss_mixer(chip);
 567        if (err < 0)
 568                goto error;
 569
 570        err = snd_wss_timer(chip, 0);
 571        if (err < 0)
 572                goto error;
 573
 574        if (mpu_port[n] >= 0) {
 575                err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
 576                                          mpu_port[n], 0, mpu_irq[n], NULL);
 577                if (err < 0)
 578                        goto error;
 579        }
 580
 581        if (fm_port[n] >= 0) {
 582                struct snd_opl3 *opl3;
 583
 584                err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
 585                                      OPL3_HW_AUTO, 0, &opl3);
 586                if (err < 0) {
 587                        dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
 588                        goto error;
 589                }
 590                err = snd_opl3_timer_new(opl3, 1, 2);
 591                if (err < 0)
 592                        goto error;
 593
 594                err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
 595                if (err < 0)
 596                        goto error;
 597        }
 598
 599        err = snd_card_register(card);
 600        if (err < 0)
 601                goto error;
 602
 603        dev_set_drvdata(dev, card);
 604        return 0;
 605
 606error:
 607        snd_card_free(card);
 608        return err;
 609}
 610
 611static int snd_galaxy_remove(struct device *dev, unsigned int n)
 612{
 613        snd_card_free(dev_get_drvdata(dev));
 614        return 0;
 615}
 616
 617static struct isa_driver snd_galaxy_driver = {
 618        .match          = snd_galaxy_match,
 619        .probe          = snd_galaxy_probe,
 620        .remove         = snd_galaxy_remove,
 621
 622        .driver         = {
 623                .name   = DEV_NAME
 624        }
 625};
 626
 627module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);
 628