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