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 <linux/slab.h> 43 #include <sound/core.h> 44 #include <sound/sb.h> 45 #include <sound/initval.h> 46 47 MODULE_AUTHOR("Ash Willis"); 48 MODULE_DESCRIPTION("CS5530 Audio"); 49 MODULE_LICENSE("GPL"); 50 51 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 52 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 53 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 54 55 struct snd_cs5530 { 56 struct snd_card *card; 57 struct pci_dev *pci; 58 struct snd_sb *sb; 59 unsigned long pci_base; 60 }; 61 62 static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = { 63 {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID, 64 PCI_ANY_ID, 0, 0}, 65 {0,} 66 }; 67 68 MODULE_DEVICE_TABLE(pci, snd_cs5530_ids); 69 70 static int snd_cs5530_free(struct snd_cs5530 *chip) 71 { 72 pci_release_regions(chip->pci); 73 pci_disable_device(chip->pci); 74 kfree(chip); 75 return 0; 76 } 77 78 static int snd_cs5530_dev_free(struct snd_device *device) 79 { 80 struct snd_cs5530 *chip = device->device_data; 81 return snd_cs5530_free(chip); 82 } 83 84 static void __devexit snd_cs5530_remove(struct pci_dev *pci) 85 { 86 snd_card_free(pci_get_drvdata(pci)); 87 pci_set_drvdata(pci, NULL); 88 } 89 90 static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg) 91 { 92 outb(reg, io + 4); 93 udelay(20); 94 reg = inb(io + 5); 95 udelay(20); 96 return reg; 97 } 98 99 static int __devinit snd_cs5530_create(struct snd_card *card, 100 struct pci_dev *pci, 101 struct snd_cs5530 **rchip) 102 { 103 struct snd_cs5530 *chip; 104 unsigned long sb_base; 105 u8 irq, dma8, dma16 = 0; 106 u16 map; 107 void __iomem *mem; 108 int err; 109 110 static struct snd_device_ops ops = { 111 .dev_free = snd_cs5530_dev_free, 112 }; 113 *rchip = NULL; 114 115 err = pci_enable_device(pci); 116 if (err < 0) 117 return err; 118 119 chip = kzalloc(sizeof(*chip), GFP_KERNEL); 120 if (chip == NULL) { 121 pci_disable_device(pci); 122 return -ENOMEM; 123 } 124 125 chip->card = card; 126 chip->pci = pci; 127 128 err = pci_request_regions(pci, "CS5530"); 129 if (err < 0) { 130 kfree(chip); 131 pci_disable_device(pci); 132 return err; 133 } 134 chip->pci_base = pci_resource_start(pci, 0); 135 136 mem = pci_ioremap_bar(pci, 0); 137 if (mem == NULL) { 138 kfree(chip); 139 pci_disable_device(pci); 140 return -EBUSY; 141 } 142 143 map = readw(mem + 0x18); 144 iounmap(mem); 145 146 /* Map bits 147 0:1 * 0x20 + 0x200 = sb base 148 2 sb enable 149 3 adlib enable 150 5 MPU enable 0x330 151 6 MPU enable 0x300 152 153 The other bits may be used internally so must be masked */ 154 155 sb_base = 0x220 + 0x20 * (map & 3); 156 157 if (map & (1<<2)) 158 printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base); 159 else { 160 printk(KERN_ERR "Could not find XpressAudio!\n"); 161 snd_cs5530_free(chip); 162 return -ENODEV; 163 } 164 165 if (map & (1<<5)) 166 printk(KERN_INFO "CS5530: MPU at 0x300\n"); 167 else if (map & (1<<6)) 168 printk(KERN_INFO "CS5530: MPU at 0x330\n"); 169 170 irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F; 171 dma8 = snd_cs5530_mixer_read(sb_base, 0x81); 172 173 if (dma8 & 0x20) 174 dma16 = 5; 175 else if (dma8 & 0x40) 176 dma16 = 6; 177 else if (dma8 & 0x80) 178 dma16 = 7; 179 else { 180 printk(KERN_ERR "CS5530: No 16bit DMA enabled\n"); 181 snd_cs5530_free(chip); 182 return -ENODEV; 183 } 184 185 if (dma8 & 0x01) 186 dma8 = 0; 187 else if (dma8 & 02) 188 dma8 = 1; 189 else if (dma8 & 0x08) 190 dma8 = 3; 191 else { 192 printk(KERN_ERR "CS5530: No 8bit DMA enabled\n"); 193 snd_cs5530_free(chip); 194 return -ENODEV; 195 } 196 197 if (irq & 1) 198 irq = 9; 199 else if (irq & 2) 200 irq = 5; 201 else if (irq & 4) 202 irq = 7; 203 else if (irq & 8) 204 irq = 10; 205 else { 206 printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n"); 207 snd_cs5530_free(chip); 208 return -ENODEV; 209 } 210 211 printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, 212 dma16); 213 214 err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8, 215 dma16, SB_HW_CS5530, &chip->sb); 216 if (err < 0) { 217 printk(KERN_ERR "CS5530: Could not create SoundBlaster\n"); 218 snd_cs5530_free(chip); 219 return err; 220 } 221 222 err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm); 223 if (err < 0) { 224 printk(KERN_ERR "CS5530: Could not create PCM\n"); 225 snd_cs5530_free(chip); 226 return err; 227 } 228 229 err = snd_sbmixer_new(chip->sb); 230 if (err < 0) { 231 printk(KERN_ERR "CS5530: Could not create Mixer\n"); 232 snd_cs5530_free(chip); 233 return err; 234 } 235 236 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 237 if (err < 0) { 238 snd_cs5530_free(chip); 239 return err; 240 } 241 242 snd_card_set_dev(card, &pci->dev); 243 *rchip = chip; 244 return 0; 245 } 246 247 static int __devinit snd_cs5530_probe(struct pci_dev *pci, 248 const struct pci_device_id *pci_id) 249 { 250 static int dev; 251 struct snd_card *card; 252 struct snd_cs5530 *chip = NULL; 253 int err; 254 255 if (dev >= SNDRV_CARDS) 256 return -ENODEV; 257 if (!enable[dev]) { 258 dev++; 259 return -ENOENT; 260 } 261 262 err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); 263 264 if (err < 0) 265 return err; 266 267 err = snd_cs5530_create(card, pci, &chip); 268 if (err < 0) { 269 snd_card_free(card); 270 return err; 271 } 272 273 strcpy(card->driver, "CS5530"); 274 strcpy(card->shortname, "CS5530 Audio"); 275 sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base); 276 277 err = snd_card_register(card); 278 if (err < 0) { 279 snd_card_free(card); 280 return err; 281 } 282 pci_set_drvdata(pci, card); 283 dev++; 284 return 0; 285 } 286 287 static struct pci_driver driver = { 288 .name = "CS5530_Audio", 289 .id_table = snd_cs5530_ids, 290 .probe = snd_cs5530_probe, 291 .remove = __devexit_p(snd_cs5530_remove), 292 }; 293 294 static int __init alsa_card_cs5530_init(void) 295 { 296 return pci_register_driver(&driver); 297 } 298 299 static void __exit alsa_card_cs5530_exit(void) 300 { 301 pci_unregister_driver(&driver); 302 } 303 304 module_init(alsa_card_cs5530_init) 305 module_exit(alsa_card_cs5530_exit) 306 307