linux/sound/isa/azt2320.c
<<
>>
Prefs
   1/*
   2    card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
   3    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
   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, write to the Free Software
  17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18*/
  19
  20/*
  21    This driver should provide support for most Aztech AZT2320 based cards.
  22    Several AZT2316 chips are also supported/tested, but autoprobe doesn't
  23    work: all module option have to be set.
  24
  25    No docs available for us at Aztech headquarters !!!   Unbelievable ...
  26    No other help obtained.
  27
  28    Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS
  29    activation method (full-duplex audio!).
  30*/
  31
  32#include <sound/driver.h>
  33#include <asm/io.h>
  34#include <linux/delay.h>
  35#include <linux/init.h>
  36#include <linux/time.h>
  37#include <linux/wait.h>
  38#include <linux/pnp.h>
  39#include <linux/moduleparam.h>
  40#include <sound/core.h>
  41#include <sound/initval.h>
  42#include <sound/cs4231.h>
  43#include <sound/mpu401.h>
  44#include <sound/opl3.h>
  45
  46#define PFX "azt2320: "
  47
  48MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
  49MODULE_DESCRIPTION("Aztech Systems AZT2320");
  50MODULE_LICENSE("GPL");
  51MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
  52                "{Aztech Systems,AZT2320},"
  53                "{Aztech Systems,AZT3300},"
  54                "{Aztech Systems,AZT2320},"
  55                "{Aztech Systems,AZT3000}}");
  56
  57static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
  58static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
  59static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
  60static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* PnP setup */
  61static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
  62static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
  63static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;  /* PnP setup */
  64static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;        /* Pnp setup */
  65static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* Pnp setup */
  66static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
  67static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
  68
  69module_param_array(index, int, NULL, 0444);
  70MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
  71module_param_array(id, charp, NULL, 0444);
  72MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
  73module_param_array(enable, bool, NULL, 0444);
  74MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
  75module_param_array(port, long, NULL, 0444);
  76MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
  77module_param_array(wss_port, long, NULL, 0444);
  78MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
  79module_param_array(mpu_port, long, NULL, 0444);
  80MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
  81module_param_array(fm_port, long, NULL, 0444);
  82MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
  83module_param_array(irq, int, NULL, 0444);
  84MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
  85module_param_array(mpu_irq, int, NULL, 0444);
  86MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
  87module_param_array(dma1, int, NULL, 0444);
  88MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
  89module_param_array(dma2, int, NULL, 0444);
  90MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
  91
  92struct snd_card_azt2320 {
  93        int dev_no;
  94        struct pnp_dev *dev;
  95        struct pnp_dev *devmpu;
  96        struct snd_cs4231 *chip;
  97};
  98
  99static struct pnp_card_device_id snd_azt2320_pnpids[] = {
 100        /* PRO16V */
 101        { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
 102        /* Aztech Sound Galaxy 16 */
 103        { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
 104        /* Packard Bell Sound III 336 AM/SP */
 105        { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
 106        /* AT3300 */
 107        { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
 108        /* --- */
 109        { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
 110        /* --- */
 111        { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
 112        { .id = "" }    /* end */
 113};
 114
 115MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
 116
 117#define DRIVER_NAME     "snd-card-azt2320"
 118
 119static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
 120                                          struct pnp_card_link *card,
 121                                          const struct pnp_card_device_id *id)
 122{
 123        struct pnp_dev *pdev;
 124        struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
 125        int err;
 126
 127        if (!cfg)
 128                return -ENOMEM;
 129
 130        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
 131        if (acard->dev == NULL) {
 132                kfree(cfg);
 133                return -ENODEV;
 134        }
 135
 136        acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
 137
 138        pdev = acard->dev;
 139        pnp_init_resource_table(cfg);
 140
 141        /* override resources */
 142        if (port[dev] != SNDRV_AUTO_PORT)
 143                pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
 144        if (fm_port[dev] != SNDRV_AUTO_PORT)
 145                pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
 146        if (wss_port[dev] != SNDRV_AUTO_PORT)
 147                pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
 148        if (dma1[dev] != SNDRV_AUTO_DMA)
 149                pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
 150        if (dma2[dev] != SNDRV_AUTO_DMA)
 151                pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
 152        if (irq[dev] != SNDRV_AUTO_IRQ)
 153                pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
 154        if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
 155                snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
 156
 157        err = pnp_activate_dev(pdev);
 158        if (err < 0) {
 159                snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
 160                kfree(cfg);
 161                return err;
 162        }
 163        port[dev] = pnp_port_start(pdev, 0);
 164        fm_port[dev] = pnp_port_start(pdev, 1);
 165        wss_port[dev] = pnp_port_start(pdev, 2);
 166        dma1[dev] = pnp_dma(pdev, 0);
 167        dma2[dev] = pnp_dma(pdev, 1);
 168        irq[dev] = pnp_irq(pdev, 0);
 169
 170        pdev = acard->devmpu;
 171        if (pdev != NULL) {
 172                pnp_init_resource_table(cfg);
 173                if (mpu_port[dev] != SNDRV_AUTO_PORT)
 174                        pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
 175                if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
 176                        pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
 177                if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
 178                        snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
 179                err = pnp_activate_dev(pdev);
 180                if (err < 0)
 181                        goto __mpu_error;
 182                mpu_port[dev] = pnp_port_start(pdev, 0);
 183                mpu_irq[dev] = pnp_irq(pdev, 0);
 184        } else {
 185             __mpu_error:
 186                if (pdev) {
 187                        pnp_release_card_device(pdev);
 188                        snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
 189                }
 190                acard->devmpu = NULL;
 191                mpu_port[dev] = -1;
 192        }
 193
 194        kfree (cfg);
 195        return 0;
 196}
 197
 198/* same of snd_sbdsp_command by Jaroslav Kysela */
 199static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
 200{
 201        int i;
 202        unsigned long limit;
 203
 204        limit = jiffies + HZ / 10;
 205        for (i = 50000; i && time_after(limit, jiffies); i--)
 206                if (!(inb(port + 0x0c) & 0x80)) {
 207                        outb(val, port + 0x0c);
 208                        return 0;
 209                }
 210        return -EBUSY;
 211}
 212
 213static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
 214{
 215        int error;
 216
 217        if ((error = snd_card_azt2320_command(port, 0x09)))
 218                return error;
 219        if ((error = snd_card_azt2320_command(port, 0x00)))
 220                return error;
 221
 222        mdelay(5);
 223        return 0;
 224}
 225
 226static int __devinit snd_card_azt2320_probe(int dev,
 227                                            struct pnp_card_link *pcard,
 228                                            const struct pnp_card_device_id *pid)
 229{
 230        int error;
 231        struct snd_card *card;
 232        struct snd_card_azt2320 *acard;
 233        struct snd_cs4231 *chip;
 234        struct snd_opl3 *opl3;
 235
 236        if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
 237                                 sizeof(struct snd_card_azt2320))) == NULL)
 238                return -ENOMEM;
 239        acard = (struct snd_card_azt2320 *)card->private_data;
 240
 241        if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
 242                snd_card_free(card);
 243                return error;
 244        }
 245        snd_card_set_dev(card, &pcard->card->dev);
 246
 247        if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
 248                snd_card_free(card);
 249                return error;
 250        }
 251
 252        if ((error = snd_cs4231_create(card, wss_port[dev], -1,
 253                                       irq[dev],
 254                                       dma1[dev],
 255                                       dma2[dev],
 256                                       CS4231_HW_DETECT, 0, &chip)) < 0) {
 257                snd_card_free(card);
 258                return error;
 259        }
 260
 261        strcpy(card->driver, "AZT2320");
 262        strcpy(card->shortname, "Aztech AZT2320");
 263        sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
 264                card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
 265
 266        if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
 267                snd_card_free(card);
 268                return error;
 269        }
 270        if ((error = snd_cs4231_mixer(chip)) < 0) {
 271                snd_card_free(card);
 272                return error;
 273        }
 274        if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
 275                snd_card_free(card);
 276                return error;
 277        }
 278
 279        if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
 280                if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
 281                                mpu_port[dev], 0,
 282                                mpu_irq[dev], IRQF_DISABLED,
 283                                NULL) < 0)
 284                        snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
 285        }
 286
 287        if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
 288                if (snd_opl3_create(card,
 289                                    fm_port[dev], fm_port[dev] + 2,
 290                                    OPL3_HW_AUTO, 0, &opl3) < 0) {
 291                        snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
 292                                   fm_port[dev], fm_port[dev] + 2);
 293                } else {
 294                        if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
 295                                snd_card_free(card);
 296                                return error;
 297                        }
 298                        if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
 299                                snd_card_free(card);
 300                                return error;
 301                        }
 302                }
 303        }
 304
 305        if ((error = snd_card_register(card)) < 0) {
 306                snd_card_free(card);
 307                return error;
 308        }
 309        pnp_set_card_drvdata(pcard, card);
 310        return 0;
 311}
 312
 313static unsigned int __devinitdata azt2320_devices;
 314
 315static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
 316                                            const struct pnp_card_device_id *id)
 317{
 318        static int dev;
 319        int res;
 320
 321        for ( ; dev < SNDRV_CARDS; dev++) {
 322                if (!enable[dev])
 323                        continue;
 324                res = snd_card_azt2320_probe(dev, card, id);
 325                if (res < 0)
 326                        return res;
 327                dev++;
 328                azt2320_devices++;
 329                return 0;
 330        }
 331        return -ENODEV;
 332}
 333
 334static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
 335{
 336        snd_card_free(pnp_get_card_drvdata(pcard));
 337        pnp_set_card_drvdata(pcard, NULL);
 338}
 339
 340#ifdef CONFIG_PM
 341static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
 342{
 343        struct snd_card *card = pnp_get_card_drvdata(pcard);
 344        struct snd_card_azt2320 *acard = card->private_data;
 345        struct snd_cs4231 *chip = acard->chip;
 346
 347        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 348        chip->suspend(chip);
 349        return 0;
 350}
 351
 352static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
 353{
 354        struct snd_card *card = pnp_get_card_drvdata(pcard);
 355        struct snd_card_azt2320 *acard = card->private_data;
 356        struct snd_cs4231 *chip = acard->chip;
 357
 358        chip->resume(chip);
 359        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 360        return 0;
 361}
 362#endif
 363
 364static struct pnp_card_driver azt2320_pnpc_driver = {
 365        .flags          = PNP_DRIVER_RES_DISABLE,
 366        .name           = "azt2320",
 367        .id_table       = snd_azt2320_pnpids,
 368        .probe          = snd_azt2320_pnp_detect,
 369        .remove         = __devexit_p(snd_azt2320_pnp_remove),
 370#ifdef CONFIG_PM
 371        .suspend        = snd_azt2320_pnp_suspend,
 372        .resume         = snd_azt2320_pnp_resume,
 373#endif
 374};
 375
 376static int __init alsa_card_azt2320_init(void)
 377{
 378        int err;
 379
 380        err = pnp_register_card_driver(&azt2320_pnpc_driver);
 381        if (err)
 382                return err;
 383
 384        if (!azt2320_devices) {
 385                pnp_unregister_card_driver(&azt2320_pnpc_driver);
 386#ifdef MODULE
 387                snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
 388#endif
 389                return -ENODEV;
 390        }
 391        return 0;
 392}
 393
 394static void __exit alsa_card_azt2320_exit(void)
 395{
 396        pnp_unregister_card_driver(&azt2320_pnpc_driver);
 397}
 398
 399module_init(alsa_card_azt2320_init)
 400module_exit(alsa_card_azt2320_exit)
 401