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/cs4231.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_cs4231 *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_cs4231 *chip;
 185        struct snd_opl3 *opl3;
 186
 187        if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
 188                                 sizeof(struct snd_card_azt2320))) == NULL)
 189                return -ENOMEM;
 190        acard = (struct snd_card_azt2320 *)card->private_data;
 191
 192        if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
 193                snd_card_free(card);
 194                return error;
 195        }
 196        snd_card_set_dev(card, &pcard->card->dev);
 197
 198        if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
 199                snd_card_free(card);
 200                return error;
 201        }
 202
 203        if ((error = snd_cs4231_create(card, wss_port[dev], -1,
 204                                       irq[dev],
 205                                       dma1[dev],
 206                                       dma2[dev],
 207                                       CS4231_HW_DETECT, 0, &chip)) < 0) {
 208                snd_card_free(card);
 209                return error;
 210        }
 211
 212        strcpy(card->driver, "AZT2320");
 213        strcpy(card->shortname, "Aztech AZT2320");
 214        sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
 215                card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
 216
 217        if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
 218                snd_card_free(card);
 219                return error;
 220        }
 221        if ((error = snd_cs4231_mixer(chip)) < 0) {
 222                snd_card_free(card);
 223                return error;
 224        }
 225        if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
 226                snd_card_free(card);
 227                return error;
 228        }
 229
 230        if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
 231                if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
 232                                mpu_port[dev], 0,
 233                                mpu_irq[dev], IRQF_DISABLED,
 234                                NULL) < 0)
 235                        snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
 236        }
 237
 238        if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
 239                if (snd_opl3_create(card,
 240                                    fm_port[dev], fm_port[dev] + 2,
 241                                    OPL3_HW_AUTO, 0, &opl3) < 0) {
 242                        snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
 243                                   fm_port[dev], fm_port[dev] + 2);
 244                } else {
 245                        if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
 246                                snd_card_free(card);
 247                                return error;
 248                        }
 249                        if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
 250                                snd_card_free(card);
 251                                return error;
 252                        }
 253                }
 254        }
 255
 256        if ((error = snd_card_register(card)) < 0) {
 257                snd_card_free(card);
 258                return error;
 259        }
 260        pnp_set_card_drvdata(pcard, card);
 261        return 0;
 262}
 263
 264static unsigned int __devinitdata azt2320_devices;
 265
 266static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
 267                                            const struct pnp_card_device_id *id)
 268{
 269        static int dev;
 270        int res;
 271
 272        for ( ; dev < SNDRV_CARDS; dev++) {
 273                if (!enable[dev])
 274                        continue;
 275                res = snd_card_azt2320_probe(dev, card, id);
 276                if (res < 0)
 277                        return res;
 278                dev++;
 279                azt2320_devices++;
 280                return 0;
 281        }
 282        return -ENODEV;
 283}
 284
 285static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
 286{
 287        snd_card_free(pnp_get_card_drvdata(pcard));
 288        pnp_set_card_drvdata(pcard, NULL);
 289}
 290
 291#ifdef CONFIG_PM
 292static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
 293{
 294        struct snd_card *card = pnp_get_card_drvdata(pcard);
 295        struct snd_card_azt2320 *acard = card->private_data;
 296        struct snd_cs4231 *chip = acard->chip;
 297
 298        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 299        chip->suspend(chip);
 300        return 0;
 301}
 302
 303static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
 304{
 305        struct snd_card *card = pnp_get_card_drvdata(pcard);
 306        struct snd_card_azt2320 *acard = card->private_data;
 307        struct snd_cs4231 *chip = acard->chip;
 308
 309        chip->resume(chip);
 310        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 311        return 0;
 312}
 313#endif
 314
 315static struct pnp_card_driver azt2320_pnpc_driver = {
 316        .flags          = PNP_DRIVER_RES_DISABLE,
 317        .name           = "azt2320",
 318        .id_table       = snd_azt2320_pnpids,
 319        .probe          = snd_azt2320_pnp_detect,
 320        .remove         = __devexit_p(snd_azt2320_pnp_remove),
 321#ifdef CONFIG_PM
 322        .suspend        = snd_azt2320_pnp_suspend,
 323        .resume         = snd_azt2320_pnp_resume,
 324#endif
 325};
 326
 327static int __init alsa_card_azt2320_init(void)
 328{
 329        int err;
 330
 331        err = pnp_register_card_driver(&azt2320_pnpc_driver);
 332        if (err)
 333                return err;
 334
 335        if (!azt2320_devices) {
 336                pnp_unregister_card_driver(&azt2320_pnpc_driver);
 337#ifdef MODULE
 338                snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
 339#endif
 340                return -ENODEV;
 341        }
 342        return 0;
 343}
 344
 345static void __exit alsa_card_azt2320_exit(void)
 346{
 347        pnp_unregister_card_driver(&azt2320_pnpc_driver);
 348}
 349
 350module_init(alsa_card_azt2320_init)
 351module_exit(alsa_card_azt2320_exit)
 352
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.