linux/sound/pci/cs5530.c
<<
>>
Prefs
   1/*
   2 * cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
   3 *
   4 *      (C) Copyright 2007 Ash Willis <ashwillis@programmer.net>
   5 *      (C) Copyright 2003 Red Hat Inc <alan@lxorguk.ukuu.org.uk>
   6 *
   7 * This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did
   8 * mess with it a bit. The chip seems to have to have trouble with full duplex
   9 * mode. If we're recording in 8bit 8000kHz, say, and we then attempt to
  10 * simultaneously play back audio at 16bit 44100kHz, the device actually plays
  11 * back in the same format in which it is capturing. By forcing the chip to
  12 * always play/capture in 16/44100, we can let alsa-lib convert the samples and
  13 * that way we can hack up some full duplex audio. 
  14 * 
  15 * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
  16 * The older version (VSA1) provides fairly good soundblaster emulation
  17 * although there are a couple of bugs: large DMA buffers break record,
  18 * and the MPU event handling seems suspect. VSA2 allows the native driver
  19 * to control the AC97 audio engine directly and requires a different driver.
  20 *
  21 * Thanks to National Semiconductor for providing the needed information
  22 * on the XpressAudio(tm) internals.
  23 *
  24 * This program is free software; you can redistribute it and/or modify it
  25 * under the terms of the GNU General Public License as published by the
  26 * Free Software Foundation; either version 2, or (at your option) any
  27 * later version.
  28 *
  29 * This program is distributed in the hope that it will be useful, but
  30 * WITHOUT ANY WARRANTY; without even the implied warranty of
  31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  32 * General Public License for more details.
  33 *
  34 * TO DO:
  35 *      Investigate whether we can portably support Cognac (5520) in the
  36 *      same manner.
  37 */
  38
  39#include <linux/delay.h>
  40#include <linux/moduleparam.h>
  41#include <linux/pci.h>
  42#include <sound/core.h>
  43#include <sound/sb.h>
  44#include <sound/initval.h>
  45
  46MODULE_AUTHOR("Ash Willis");
  47MODULE_DESCRIPTION("CS5530 Audio");
  48MODULE_LICENSE("GPL");
  49
  50static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
  51static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
  52static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
  53
  54struct snd_cs5530 {
  55        struct snd_card *card;
  56        struct pci_dev *pci;
  57        struct snd_sb *sb;
  58        unsigned long pci_base;
  59};
  60
  61static struct pci_device_id snd_cs5530_ids[] = {
  62        {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
  63                                                        PCI_ANY_ID, 0, 0},
  64        {0,}
  65};
  66
  67MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
  68
  69static int snd_cs5530_free(struct snd_cs5530 *chip)
  70{
  71        pci_release_regions(chip->pci);
  72        pci_disable_device(chip->pci);
  73        kfree(chip);
  74        return 0;
  75}
  76
  77static int snd_cs5530_dev_free(struct snd_device *device)
  78{
  79        struct snd_cs5530 *chip = device->device_data;
  80        return snd_cs5530_free(chip);
  81}
  82
  83static void __devexit snd_cs5530_remove(struct pci_dev *pci)
  84{
  85        snd_card_free(pci_get_drvdata(pci));
  86        pci_set_drvdata(pci, NULL);
  87}
  88
  89static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
  90{
  91        outb(reg, io + 4);
  92        udelay(20);
  93        reg = inb(io + 5);
  94        udelay(20);
  95        return reg;
  96}
  97
  98static int __devinit snd_cs5530_create(struct snd_card *card,
  99                                       struct pci_dev *pci,
 100                                       struct snd_cs5530 **rchip)
 101{
 102        struct snd_cs5530 *chip;
 103        unsigned long sb_base;
 104        u8 irq, dma8, dma16 = 0;
 105        u16 map;
 106        void __iomem *mem;
 107        int err;
 108
 109        static struct snd_device_ops ops = {
 110                .dev_free = snd_cs5530_dev_free,
 111        };
 112        *rchip = NULL;
 113
 114        err = pci_enable_device(pci);
 115        if (err < 0)
 116                return err;
 117
 118        chip = kzalloc(sizeof(*chip), GFP_KERNEL);
 119        if (chip == NULL) {
 120                pci_disable_device(pci);
 121                return -ENOMEM;
 122        }
 123
 124        chip->card = card;
 125        chip->pci = pci;
 126
 127        err = pci_request_regions(pci, "CS5530");
 128        if (err < 0) {
 129                kfree(chip); 
 130                pci_disable_device(pci);
 131                return err;
 132        }
 133        chip->pci_base = pci_resource_start(pci, 0);
 134
 135        mem = pci_ioremap_bar(pci, 0);
 136        if (mem == NULL) {
 137                kfree(chip);
 138                pci_disable_device(pci);
 139                return -EBUSY;
 140        }
 141
 142        map = readw(mem + 0x18);
 143        iounmap(mem);
 144
 145        /* Map bits
 146                0:1     * 0x20 + 0x200 = sb base
 147                2       sb enable
 148                3       adlib enable
 149                5       MPU enable 0x330
 150                6       MPU enable 0x300
 151
 152           The other bits may be used internally so must be masked */
 153
 154        sb_base = 0x220 + 0x20 * (map & 3);
 155
 156        if (map & (1<<2))
 157                printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
 158        else {
 159                printk(KERN_ERR "Could not find XpressAudio!\n");
 160                snd_cs5530_free(chip);
 161                return -ENODEV;
 162        }
 163
 164        if (map & (1<<5))
 165                printk(KERN_INFO "CS5530: MPU at 0x300\n");
 166        else if (map & (1<<6))
 167                printk(KERN_INFO "CS5530: MPU at 0x330\n");
 168
 169        irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
 170        dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
 171
 172        if (dma8 & 0x20)
 173                dma16 = 5;
 174        else if (dma8 & 0x40)
 175                dma16 = 6;
 176        else if (dma8 & 0x80)
 177                dma16 = 7;
 178        else {
 179                printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
 180                snd_cs5530_free(chip);
 181                return -ENODEV;
 182        }
 183
 184        if (dma8 & 0x01)
 185                dma8 = 0;
 186        else if (dma8 & 02)
 187                dma8 = 1;
 188        else if (dma8 & 0x08)
 189                dma8 = 3;
 190        else {
 191                printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
 192                snd_cs5530_free(chip);
 193                return -ENODEV;
 194        }
 195
 196        if (irq & 1)
 197                irq = 9;
 198        else if (irq & 2)
 199                irq = 5;
 200        else if (irq & 4)
 201                irq = 7;
 202        else if (irq & 8)
 203                irq = 10;
 204        else {
 205                printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
 206                snd_cs5530_free(chip);
 207                return -ENODEV;
 208        }
 209
 210        printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, 
 211                                                                        dma16);
 212
 213        err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
 214                                                dma16, SB_HW_CS5530, &chip->sb);
 215        if (err < 0) {
 216                printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
 217                snd_cs5530_free(chip);
 218                return err;
 219        }
 220
 221        err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
 222        if (err < 0) {
 223                printk(KERN_ERR "CS5530: Could not create PCM\n");
 224                snd_cs5530_free(chip);
 225                return err;
 226        }
 227
 228        err = snd_sbmixer_new(chip->sb);
 229        if (err < 0) {
 230                printk(KERN_ERR "CS5530: Could not create Mixer\n");
 231                snd_cs5530_free(chip);
 232                return err;
 233        }
 234
 235        err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
 236        if (err < 0) {
 237                snd_cs5530_free(chip);
 238                return err;
 239        }
 240
 241        snd_card_set_dev(card, &pci->dev);
 242        *rchip = chip;
 243        return 0;
 244}
 245
 246static int __devinit snd_cs5530_probe(struct pci_dev *pci,
 247                                        const struct pci_device_id *pci_id)
 248{
 249        static int dev;
 250        struct snd_card *card;
 251        struct snd_cs5530 *chip = NULL;
 252        int err;
 253
 254        if (dev >= SNDRV_CARDS)
 255                return -ENODEV;
 256        if (!enable[dev]) {
 257                dev++;
 258                return -ENOENT;
 259        }
 260
 261        card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
 262
 263        if (card == NULL)
 264                return -ENOMEM;
 265
 266        err = snd_cs5530_create(card, pci, &chip);
 267        if (err < 0) {
 268                snd_card_free(card);
 269                return err;
 270        }
 271
 272        strcpy(card->driver, "CS5530");
 273        strcpy(card->shortname, "CS5530 Audio");
 274        sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
 275
 276        err = snd_card_register(card);
 277        if (err < 0) {
 278                snd_card_free(card);
 279                return err;
 280        }
 281        pci_set_drvdata(pci, card);
 282        dev++;
 283        return 0;
 284}
 285
 286static struct pci_driver driver = {
 287        .name = "CS5530_Audio",
 288        .id_table = snd_cs5530_ids,
 289        .probe = snd_cs5530_probe,
 290        .remove = __devexit_p(snd_cs5530_remove),
 291};
 292
 293static int __init alsa_card_cs5530_init(void)
 294{
 295        return pci_register_driver(&driver);
 296}
 297
 298static void __exit alsa_card_cs5530_exit(void)
 299{
 300        pci_unregister_driver(&driver);
 301}
 302
 303module_init(alsa_card_cs5530_init)
 304module_exit(alsa_card_cs5530_exit)
 305
 306