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 <asm/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/moduleparam.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 int 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 __devinit 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 __devinit 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 __devinit 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 __devinit 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_create(index[dev], id[dev], THIS_MODULE,
 188                                sizeof(struct snd_card_azt2320), &card);
 189        if (error < 0)
 190                return error;
 191        acard = card->private_data;
 192
 193        if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
 194                snd_card_free(card);
 195                return error;
 196        }
 197        snd_card_set_dev(card, &pcard->card->dev);
 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, NULL);
 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, NULL);
 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], IRQF_DISABLED,
 238                                NULL) < 0)
 239                        snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
 240        }
 241
 242        if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
 243                if (snd_opl3_create(card,
 244                                    fm_port[dev], fm_port[dev] + 2,
 245                                    OPL3_HW_AUTO, 0, &opl3) < 0) {
 246                        snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
 247                                   fm_port[dev], fm_port[dev] + 2);
 248                } else {
 249                        if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
 250                                snd_card_free(card);
 251                                return error;
 252                        }
 253                        if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
 254                                snd_card_free(card);
 255                                return error;
 256                        }
 257                }
 258        }
 259
 260        if ((error = snd_card_register(card)) < 0) {
 261                snd_card_free(card);
 262                return error;
 263        }
 264        pnp_set_card_drvdata(pcard, card);
 265        return 0;
 266}
 267
 268static unsigned int __devinitdata azt2320_devices;
 269
 270static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
 271                                            const struct pnp_card_device_id *id)
 272{
 273        static int dev;
 274        int res;
 275
 276        for ( ; dev < SNDRV_CARDS; dev++) {
 277                if (!enable[dev])
 278                        continue;
 279                res = snd_card_azt2320_probe(dev, card, id);
 280                if (res < 0)
 281                        return res;
 282                dev++;
 283                azt2320_devices++;
 284                return 0;
 285        }
 286        return -ENODEV;
 287}
 288
 289static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
 290{
 291        snd_card_free(pnp_get_card_drvdata(pcard));
 292        pnp_set_card_drvdata(pcard, NULL);
 293}
 294
 295#ifdef CONFIG_PM
 296static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
 297{
 298        struct snd_card *card = pnp_get_card_drvdata(pcard);
 299        struct snd_card_azt2320 *acard = card->private_data;
 300        struct snd_wss *chip = acard->chip;
 301
 302        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 303        chip->suspend(chip);
 304        return 0;
 305}
 306
 307static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
 308{
 309        struct snd_card *card = pnp_get_card_drvdata(pcard);
 310        struct snd_card_azt2320 *acard = card->private_data;
 311        struct snd_wss *chip = acard->chip;
 312
 313        chip->resume(chip);
 314        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 315        return 0;
 316}
 317#endif
 318
 319static struct pnp_card_driver azt2320_pnpc_driver = {
 320        .flags          = PNP_DRIVER_RES_DISABLE,
 321        .name           = "azt2320",
 322        .id_table       = snd_azt2320_pnpids,
 323        .probe          = snd_azt2320_pnp_detect,
 324        .remove         = __devexit_p(snd_azt2320_pnp_remove),
 325#ifdef CONFIG_PM
 326        .suspend        = snd_azt2320_pnp_suspend,
 327        .resume         = snd_azt2320_pnp_resume,
 328#endif
 329};
 330
 331static int __init alsa_card_azt2320_init(void)
 332{
 333        int err;
 334
 335        err = pnp_register_card_driver(&azt2320_pnpc_driver);
 336        if (err)
 337                return err;
 338
 339        if (!azt2320_devices) {
 340                pnp_unregister_card_driver(&azt2320_pnpc_driver);
 341#ifdef MODULE
 342                snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
 343#endif
 344                return -ENODEV;
 345        }
 346        return 0;
 347}
 348
 349static void __exit alsa_card_azt2320_exit(void)
 350{
 351        pnp_unregister_card_driver(&azt2320_pnpc_driver);
 352}
 353
 354module_init(alsa_card_azt2320_init)
 355module_exit(alsa_card_azt2320_exit)
 356