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 <linux/io.h>
  33#include <linux/delay.h>
  34#include <linux/init.h>
  35#include <linux/time.h>
  36#include <linux/wait.h>
  37#include <linux/pnp.h>
  38#include <linux/module.h>
  39#include <sound/core.h>
  40#include <sound/initval.h>
  41#include <sound/wss.h>
  42#include <sound/mpu401.h>
  43#include <sound/opl3.h>
  44
  45#define PFX "azt2320: "
  46
  47MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
  48MODULE_DESCRIPTION("Aztech Systems AZT2320");
  49MODULE_LICENSE("GPL");
  50MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
  51                "{Aztech Systems,AZT2320},"
  52                "{Aztech Systems,AZT3300},"
  53                "{Aztech Systems,AZT2320},"
  54                "{Aztech Systems,AZT3000}}");
  55
  56static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
  57static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
  58static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
  59static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* PnP setup */
  60static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
  61static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
  62static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;  /* PnP setup */
  63static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;        /* Pnp setup */
  64static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* Pnp setup */
  65static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
  66static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
  67
  68module_param_array(index, int, NULL, 0444);
  69MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
  70module_param_array(id, charp, NULL, 0444);
  71MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
  72module_param_array(enable, bool, NULL, 0444);
  73MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
  74
  75struct snd_card_azt2320 {
  76        int dev_no;
  77        struct pnp_dev *dev;
  78        struct pnp_dev *devmpu;
  79        struct snd_wss *chip;
  80};
  81
  82static struct pnp_card_device_id snd_azt2320_pnpids[] = {
  83        /* PRO16V */
  84        { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
  85        /* Aztech Sound Galaxy 16 */
  86        { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
  87        /* Packard Bell Sound III 336 AM/SP */
  88        { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
  89        /* AT3300 */
  90        { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
  91        /* --- */
  92        { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
  93        /* --- */
  94        { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
  95        { .id = "" }    /* end */
  96};
  97
  98MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
  99
 100#define DRIVER_NAME     "snd-card-azt2320"
 101
 102static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
 103                                struct pnp_card_link *card,
 104                                const struct pnp_card_device_id *id)
 105{
 106        struct pnp_dev *pdev;
 107        int err;
 108
 109        acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
 110        if (acard->dev == NULL)
 111                return -ENODEV;
 112
 113        acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
 114
 115        pdev = acard->dev;
 116
 117        err = pnp_activate_dev(pdev);
 118        if (err < 0) {
 119                snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
 120                return err;
 121        }
 122        port[dev] = pnp_port_start(pdev, 0);
 123        fm_port[dev] = pnp_port_start(pdev, 1);
 124        wss_port[dev] = pnp_port_start(pdev, 2);
 125        dma1[dev] = pnp_dma(pdev, 0);
 126        dma2[dev] = pnp_dma(pdev, 1);
 127        irq[dev] = pnp_irq(pdev, 0);
 128
 129        pdev = acard->devmpu;
 130        if (pdev != NULL) {
 131                err = pnp_activate_dev(pdev);
 132                if (err < 0)
 133                        goto __mpu_error;
 134                mpu_port[dev] = pnp_port_start(pdev, 0);
 135                mpu_irq[dev] = pnp_irq(pdev, 0);
 136        } else {
 137             __mpu_error:
 138                if (pdev) {
 139                        pnp_release_card_device(pdev);
 140                        snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
 141                }
 142                acard->devmpu = NULL;
 143                mpu_port[dev] = -1;
 144        }
 145
 146        return 0;
 147}
 148
 149/* same of snd_sbdsp_command by Jaroslav Kysela */
 150static int snd_card_azt2320_command(unsigned long port, unsigned char val)
 151{
 152        int i;
 153        unsigned long limit;
 154
 155        limit = jiffies + HZ / 10;
 156        for (i = 50000; i && time_after(limit, jiffies); i--)
 157                if (!(inb(port + 0x0c) & 0x80)) {
 158                        outb(val, port + 0x0c);
 159                        return 0;
 160                }
 161        return -EBUSY;
 162}
 163
 164static int snd_card_azt2320_enable_wss(unsigned long port)
 165{
 166        int error;
 167
 168        if ((error = snd_card_azt2320_command(port, 0x09)))
 169                return error;
 170        if ((error = snd_card_azt2320_command(port, 0x00)))
 171                return error;
 172
 173        mdelay(5);
 174        return 0;
 175}
 176
 177static int snd_card_azt2320_probe(int dev,
 178                                  struct pnp_card_link *pcard,
 179                                  const struct pnp_card_device_id *pid)
 180{
 181        int error;
 182        struct snd_card *card;
 183        struct snd_card_azt2320 *acard;
 184        struct snd_wss *chip;
 185        struct snd_opl3 *opl3;
 186
 187        error = snd_card_new(&pcard->card->dev,
 188                             index[dev], id[dev], THIS_MODULE,
 189                             sizeof(struct snd_card_azt2320), &card);
 190        if (error < 0)
 191                return error;
 192        acard = card->private_data;
 193
 194        if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
 195                snd_card_free(card);
 196                return error;
 197        }
 198
 199        if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
 200                snd_card_free(card);
 201                return error;
 202        }
 203
 204        error = snd_wss_create(card, wss_port[dev], -1,
 205                               irq[dev],
 206                               dma1[dev], dma2[dev],
 207                               WSS_HW_DETECT, 0, &chip);
 208        if (error < 0) {
 209                snd_card_free(card);
 210                return error;
 211        }
 212
 213        strcpy(card->driver, "AZT2320");
 214        strcpy(card->shortname, "Aztech AZT2320");
 215        sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
 216                card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
 217
 218        error = snd_wss_pcm(chip, 0);
 219        if (error < 0) {
 220                snd_card_free(card);
 221                return error;
 222        }
 223        error = snd_wss_mixer(chip);
 224        if (error < 0) {
 225                snd_card_free(card);
 226                return error;
 227        }
 228        error = snd_wss_timer(chip, 0);
 229        if (error < 0) {
 230                snd_card_free(card);
 231                return error;
 232        }
 233
 234        if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
 235                if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
 236                                mpu_port[dev], 0,
 237                                mpu_irq[dev], NULL) < 0)
 238                        snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
 239        }
 240
 241        if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
 242                if (snd_opl3_create(card,
 243                                    fm_port[dev], fm_port[dev] + 2,
 244                                    OPL3_HW_AUTO, 0, &opl3) < 0) {
 245                        snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
 246                                   fm_port[dev], fm_port[dev] + 2);
 247                } else {
 248                        if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
 249                                snd_card_free(card);
 250                                return error;
 251                        }
 252                        if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
 253                                snd_card_free(card);
 254                                return error;
 255                        }
 256                }
 257        }
 258
 259        if ((error = snd_card_register(card)) < 0) {
 260                snd_card_free(card);
 261                return error;
 262        }
 263        pnp_set_card_drvdata(pcard, card);
 264        return 0;
 265}
 266
 267static unsigned int azt2320_devices;
 268
 269static int snd_azt2320_pnp_detect(struct pnp_card_link *card,
 270                                  const struct pnp_card_device_id *id)
 271{
 272        static int dev;
 273        int res;
 274
 275        for ( ; dev < SNDRV_CARDS; dev++) {
 276                if (!enable[dev])
 277                        continue;
 278                res = snd_card_azt2320_probe(dev, card, id);
 279                if (res < 0)
 280                        return res;
 281                dev++;
 282                azt2320_devices++;
 283                return 0;
 284        }
 285        return -ENODEV;
 286}
 287
 288static void snd_azt2320_pnp_remove(struct pnp_card_link *pcard)
 289{
 290        snd_card_free(pnp_get_card_drvdata(pcard));
 291        pnp_set_card_drvdata(pcard, NULL);
 292}
 293
 294#ifdef CONFIG_PM
 295static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
 296{
 297        struct snd_card *card = pnp_get_card_drvdata(pcard);
 298        struct snd_card_azt2320 *acard = card->private_data;
 299        struct snd_wss *chip = acard->chip;
 300
 301        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 302        chip->suspend(chip);
 303        return 0;
 304}
 305
 306static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
 307{
 308        struct snd_card *card = pnp_get_card_drvdata(pcard);
 309        struct snd_card_azt2320 *acard = card->private_data;
 310        struct snd_wss *chip = acard->chip;
 311
 312        chip->resume(chip);
 313        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 314        return 0;
 315}
 316#endif
 317
 318static struct pnp_card_driver azt2320_pnpc_driver = {
 319        .flags          = PNP_DRIVER_RES_DISABLE,
 320        .name           = "azt2320",
 321        .id_table       = snd_azt2320_pnpids,
 322        .probe          = snd_azt2320_pnp_detect,
 323        .remove         = snd_azt2320_pnp_remove,
 324#ifdef CONFIG_PM
 325        .suspend        = snd_azt2320_pnp_suspend,
 326        .resume         = snd_azt2320_pnp_resume,
 327#endif
 328};
 329
 330static int __init alsa_card_azt2320_init(void)
 331{
 332        int err;
 333
 334        err = pnp_register_card_driver(&azt2320_pnpc_driver);
 335        if (err)
 336                return err;
 337
 338        if (!azt2320_devices) {
 339                pnp_unregister_card_driver(&azt2320_pnpc_driver);
 340#ifdef MODULE
 341                snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
 342#endif
 343                return -ENODEV;
 344        }
 345        return 0;
 346}
 347
 348static void __exit alsa_card_azt2320_exit(void)
 349{
 350        pnp_unregister_card_driver(&azt2320_pnpc_driver);
 351}
 352
 353module_init(alsa_card_azt2320_init)
 354module_exit(alsa_card_azt2320_exit)
 355