patch-2.3.43 linux/drivers/sound/sonicvibes.c

Next file: linux/drivers/sound/soundcard.c
Previous file: linux/drivers/sound/sb_card.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.42/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
@@ -87,6 +87,7 @@
  *    08.01.2000   0.25  Prevent some ioctl's from returning bad count values on underrun/overrun;
  *                       Tim Janik's BSE (Bedevilled Sound Engine) found this
  *                       use Martin Mares' pci_assign_resource
+ *    07.02.2000   0.26  Use pci_alloc_consistent and pci_register_driver
  *
  */
 
@@ -287,8 +288,11 @@
 	/* magic */
 	unsigned int magic;
 
-	/* we keep sv cards in a linked list */
-	struct sv_state *next;
+	/* list of sonicvibes devices */
+	struct list_head devs;
+
+	/* the corresponding pci_dev structure */
+	struct pci_dev *dev;
 
 	/* soundcore stuff */
 	int dev_audio;
@@ -319,6 +323,7 @@
 
 	struct dmabuf {
 		void *rawbuf;
+		dma_addr_t dmaaddr;
 		unsigned buforder;
 		unsigned numfrag;
 		unsigned fragshift;
@@ -354,7 +359,7 @@
 
 /* --------------------------------------------------------------------- */
 
-static struct sv_state *devs = NULL;
+static LIST_HEAD(devs);
 static unsigned long wavetable_mem = 0;
 
 /* --------------------------------------------------------------------- */
@@ -684,7 +689,7 @@
 #define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
 #define DMABUF_MINORDER 1
 
-static void dealloc_dmabuf(struct dmabuf *db)
+static void dealloc_dmabuf(struct sv_state *s, struct dmabuf *db)
 {
 	unsigned long map, mapend;
 
@@ -693,7 +698,7 @@
 		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
 		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
 			clear_bit(PG_reserved, &mem_map[map].flags);	
-		free_pages((unsigned long)db->rawbuf, db->buforder);
+		pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
 	}
 	db->rawbuf = NULL;
 	db->mapped = db->ready = 0;
@@ -729,7 +734,7 @@
 	if (!db->rawbuf) {
 		db->ready = db->mapped = 0;
 		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
-			if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))
+			if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
 				break;
 		if (!db->rawbuf)
 			return -ENOMEM;
@@ -770,12 +775,12 @@
 	memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
 	spin_lock_irqsave(&s->lock, flags);
 	if (rec) {
-		set_dmac(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift);
+		set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift);
 		/* program enhanced mode registers */
 		wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8);
 		wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1);
 	} else {
-		set_dmaa(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift);
+		set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift);
 		/* program enhanced mode registers */
 		wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8);
 		wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1);
@@ -1223,12 +1228,16 @@
 static int sv_open_mixdev(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
-	struct sv_state *s = devs;
+	struct list_head *list;
+	struct sv_state *s;
 
-	while (s && s->dev_mixer != minor)
-		s = s->next;
-	if (!s)
-		return -ENODEV;
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (s->dev_mixer == minor)
+			break;
+	}
        	VALIDATE_STATE(s);
 	file->private_data = s;
 	MOD_INC_USE_COUNT;
@@ -1250,19 +1259,10 @@
 }
 
 static /*const*/ struct file_operations sv_mixer_fops = {
-	&sv_llseek,
-	NULL,  /* read */
-	NULL,  /* write */
-	NULL,  /* readdir */
-	NULL,  /* poll */
-	&sv_ioctl_mixdev,
-	NULL,  /* mmap */
-	&sv_open_mixdev,
-	NULL,	/* flush */
-	&sv_release_mixdev,
-	NULL,  /* fsync */
-	NULL,  /* fasync */
-	NULL,  /* lock */
+	llseek:		sv_llseek,
+	ioctl:		sv_ioctl_mixdev,
+	open:		sv_open_mixdev,
+	release:	sv_release_mixdev,
 };
 
 /* --------------------------------------------------------------------- */
@@ -1852,13 +1852,17 @@
 {
 	int minor = MINOR(inode->i_rdev);
 	DECLARE_WAITQUEUE(wait, current);
-	struct sv_state *s = devs;
 	unsigned char fmtm = ~0, fmts = 0;
+	struct list_head *list;
+	struct sv_state *s;
 
-	while (s && ((s->dev_audio ^ minor) & ~0xf))
-		s = s->next;
-	if (!s)
-		return -ENODEV;
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (!((s->dev_audio ^ minor) & ~0xf))
+			break;
+	}
        	VALIDATE_STATE(s);
 	file->private_data = s;
 	/* wait for device to become free */
@@ -1909,11 +1913,11 @@
 	down(&s->open_sem);
 	if (file->f_mode & FMODE_WRITE) {
 		stop_dac(s);
-		dealloc_dmabuf(&s->dma_dac);
+		dealloc_dmabuf(s, &s->dma_dac);
 	}
 	if (file->f_mode & FMODE_READ) {
 		stop_adc(s);
-		dealloc_dmabuf(&s->dma_adc);
+		dealloc_dmabuf(s, &s->dma_adc);
 	}
 	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
 	wake_up(&s->open_wait);
@@ -1923,19 +1927,14 @@
 }
 
 static /*const*/ struct file_operations sv_audio_fops = {
-	&sv_llseek,
-	&sv_read,
-	&sv_write,
-	NULL,  /* readdir */
-	&sv_poll,
-	&sv_ioctl,
-	&sv_mmap,
-	&sv_open,
-	NULL,	/* flush */
-	&sv_release,
-	NULL,  /* fsync */
-	NULL,  /* fasync */
-	NULL,  /* lock */
+	llseek:		sv_llseek,
+	read:		sv_read,
+	write:		sv_write,
+	poll:		sv_poll,
+	ioctl:		sv_ioctl,
+	mmap:		sv_mmap,
+	open:		sv_open,
+	release:	sv_release,
 };
 
 /* --------------------------------------------------------------------- */
@@ -2098,13 +2097,17 @@
 {
 	int minor = MINOR(inode->i_rdev);
 	DECLARE_WAITQUEUE(wait, current);
-	struct sv_state *s = devs;
 	unsigned long flags;
+	struct list_head *list;
+	struct sv_state *s;
 
-	while (s && s->dev_midi != minor)
-		s = s->next;
-	if (!s)
-		return -ENODEV;
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (s->dev_midi == minor)
+			break;
+	}
        	VALIDATE_STATE(s);
 	file->private_data = s;
 	/* wait for device to become free */
@@ -2203,19 +2206,12 @@
 }
 
 static /*const*/ struct file_operations sv_midi_fops = {
-	&sv_llseek,
-	&sv_midi_read,
-	&sv_midi_write,
-	NULL,  /* readdir */
-	&sv_midi_poll,
-	NULL,  /* ioctl */
-	NULL,  /* mmap */
-	&sv_midi_open,
-	NULL,	/* flush */
-	&sv_midi_release,
-	NULL,  /* fsync */
-	NULL,  /* fasync */
-	NULL,  /* lock */
+	llseek:		sv_llseek,
+	read:		sv_midi_read,
+	write:		sv_midi_write,
+	poll:		sv_midi_poll,
+	open:		sv_midi_open,
+	release:	sv_midi_release,
 };
 
 /* --------------------------------------------------------------------- */
@@ -2321,12 +2317,16 @@
 {
 	int minor = MINOR(inode->i_rdev);
 	DECLARE_WAITQUEUE(wait, current);
-	struct sv_state *s = devs;
+	struct list_head *list;
+	struct sv_state *s;
 
-	while (s && s->dev_dmfm != minor)
-		s = s->next;
-	if (!s)
-		return -ENODEV;
+	for (list = devs.next; ; list = list->next) {
+		if (list == &devs)
+			return -ENODEV;
+		s = list_entry(list, struct sv_state, devs);
+		if (s->dev_dmfm == minor)
+			break;
+	}
        	VALIDATE_STATE(s);
 	file->private_data = s;
 	/* wait for device to become free */
@@ -2380,24 +2380,15 @@
 }
 
 static /*const*/ struct file_operations sv_dmfm_fops = {
-	&sv_llseek,
-	NULL,  /* read */
-	NULL,  /* write */
-	NULL,  /* readdir */
-	NULL,  /* poll */
-	&sv_dmfm_ioctl,
-	NULL,  /* mmap */
-	&sv_dmfm_open,
-	NULL,	/* flush */
-	&sv_dmfm_release,
-	NULL,  /* fsync */
-	NULL,  /* fasync */
-	NULL,  /* lock */
+	llseek:		sv_llseek,
+	ioctl:		sv_dmfm_ioctl,
+	open:		sv_dmfm_open,
+	release:	sv_dmfm_release,
 };
 
 /* --------------------------------------------------------------------- */
 
-/* maximum number of devices */
+/* maximum number of devices; only used for command line params */
 #define NR_DEVICE 5
 
 static int reverb[NR_DEVICE] = { 0, };
@@ -2406,6 +2397,8 @@
 static int wavetable[NR_DEVICE] = { 0, };
 #endif
 
+static unsigned int devindex = 0;
+
 MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i");
 MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM");
 #if 0
@@ -2437,216 +2430,235 @@
 				 ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
 #define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
 
-static int __init init_sonicvibes(void)
+static int sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
 {
 	static const char __initlocaldata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller";
        	struct sv_state *s;
-	struct pci_dev *pcidev = NULL;
 	mm_segment_t fs;
-	int i, val, index = 0;
+	int i, val;
 	char *ddmaname;
 	unsigned ddmanamelen;
 
+	if (!RSRCISIOREGION(pcidev, RESOURCE_SB) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_ENH) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_MIDI) ||
+	    !RSRCISIOREGION(pcidev, RESOURCE_GAME))
+		return -1;
+	if (pcidev->irq == 0)
+		return -1;
+	/* try to allocate a DDMA resource if not already available */
+	if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) {
+		pcidev->resource[RESOURCE_DDMA].start = 0;
+		pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1;
+		pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO;
+		ddmanamelen = strlen(sv_ddma_name)+1;
+		if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL)))
+			return -1;
+		memcpy(ddmaname, sv_ddma_name, ddmanamelen);
+		pcidev->resource[RESOURCE_DDMA].name = ddmaname;
+		if (pci_assign_resource(pcidev, RESOURCE_DDMA)) {
+			pcidev->resource[RESOURCE_DDMA].name = NULL;
+			kfree(ddmaname);
+			printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n");
+			return -1;
+		}
+	}
+	if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) {
+		printk(KERN_WARNING "sv: out of memory\n");
+		return -1;
+	}
+	memset(s, 0, sizeof(struct sv_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+	s->magic = SV_MAGIC;
+	s->dev = pcidev;
+	s->iosb = RSRCADDRESS(pcidev, RESOURCE_SB);
+	s->ioenh = RSRCADDRESS(pcidev, RESOURCE_ENH);
+	s->iosynth = RSRCADDRESS(pcidev, RESOURCE_SYNTH);
+	s->iomidi = RSRCADDRESS(pcidev, RESOURCE_MIDI);
+	s->iogame = RSRCADDRESS(pcidev, RESOURCE_GAME);
+	s->iodmaa = RSRCADDRESS(pcidev, RESOURCE_DDMA);
+	s->iodmac = RSRCADDRESS(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA;
+	pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9);  /* enable and use extended mode */
+	pci_write_config_dword(pcidev, 0x48, s->iodmac | 9);  /* enable */
+	printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#lx %#x %#x\n",
+	       s->iosb, s->ioenh, s->iosynth, s->iomidi, s->iogame, s->iodmaa, s->iodmac);
+	s->irq = pcidev->irq;
+	
+	/* hack */
+	pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12);  /* wavetable base address */
+	
+	if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) {
+		printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1);
+		goto err_region5;
+	}
+	if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) {
+		printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1);
+		goto err_region4;
+	}
+	if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) {
+		printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1);
+		goto err_region3;
+	}
+	if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) {
+		printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1);
+		goto err_region2;
+	}
+	if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) {
+		printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1);
+		goto err_region1;
+	}
+	pci_enable_device(pcidev);
+	/* initialize codec registers */
+	outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */
+	udelay(50);
+	outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */
+	udelay(50);
+	outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */
+	     | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL);
+	inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */
+	wrindir(s, SV_CIDRIVECONTROL, 0);  /* drive current 16mA */
+	wrindir(s, SV_CIENABLE, s->enable = 0);  /* disable DMAA and DMAC */
+	outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK);
+	/* outb(0xff, s->iodmaa + SV_DMA_RESET); */
+	/* outb(0xff, s->iodmac + SV_DMA_RESET); */
+	inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
+	wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */
+	wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */
+	wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */
+	setpll(s, SV_CIADCPLLM, 8000);
+	wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */
+	wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff);
+	wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff);
+	wrindir(s, SV_CIADCOUTPUT, 0);
+	/* request irq */
+	if (request_irq(s->irq, sv_interrupt, SA_SHIRQ, "S3 SonicVibes", s)) {
+		printk(KERN_ERR "sv: irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+	printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n",
+	       s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
+	/* register devices */
+	if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0)
+		goto err_dev1;
+	if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0)
+		goto err_dev2;
+	if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0)
+		goto err_dev3;
+	if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0)
+		goto err_dev4;
+	pci_set_master(pcidev);  /* enable bus mastering */
+	/* initialize the chips */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE|SOUND_MASK_SYNTH;
+	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+	for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+	}
+	set_fs(fs);
+       /* store it in the driver field */
+	pcidev->driver_data = s;
+	pcidev->dma_mask = 0x00ffffff;
+	/* put it into driver list */
+	list_add_tail(&s->devs, &devs);
+	/* increment devindex */
+	if (devindex < NR_DEVICE-1)
+		devindex++;
+	return 0;
+
+ err_dev4:
+	unregister_sound_midi(s->dev_midi);
+ err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR "sv: cannot register misc device\n");
+	free_irq(s->irq, s);
+ err_irq:
+	release_region(s->iosynth, SV_EXTENT_SYNTH);
+ err_region1:
+	release_region(s->iomidi, SV_EXTENT_MIDI);
+ err_region2:
+	release_region(s->iodmac, SV_EXTENT_DMA);
+ err_region3:
+	release_region(s->iodmaa, SV_EXTENT_DMA);
+ err_region4:
+	release_region(s->ioenh, SV_EXTENT_ENH);
+ err_region5:
+	kfree_s(s, sizeof(struct sv_state));
+	return -1;
+}
+
+static void sv_remove(struct pci_dev *dev)
+{
+       struct sv_state *s = (struct sv_state *)dev->driver_data;
+
+       if (!s)
+               return;
+       list_del(&s->devs);
+       outb(~0, s->ioenh + SV_CODEC_INTMASK);  /* disable ints */
+       synchronize_irq();
+       inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
+       wrindir(s, SV_CIENABLE, 0);     /* disable DMAA and DMAC */
+       /*outb(0, s->iodmaa + SV_DMA_RESET);*/
+       /*outb(0, s->iodmac + SV_DMA_RESET);*/
+       free_irq(s->irq, s);
+       release_region(s->iodmac, SV_EXTENT_DMA);
+       release_region(s->iodmaa, SV_EXTENT_DMA);
+       release_region(s->ioenh, SV_EXTENT_ENH);
+       release_region(s->iomidi, SV_EXTENT_MIDI);
+       release_region(s->iosynth, SV_EXTENT_SYNTH);
+       unregister_sound_dsp(s->dev_audio);
+       unregister_sound_mixer(s->dev_mixer);
+       unregister_sound_midi(s->dev_midi);
+       unregister_sound_special(s->dev_dmfm);
+       kfree_s(s, sizeof(struct sv_state));
+       dev->driver_data = NULL;
+}
+
+static const struct pci_device_id id_table[] = {
+       { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+       { 0, 0, 0, 0, 0, 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, id_table);
+
+static struct pci_driver sv_driver = {
+       name: "sonicvibes",
+       id_table: id_table,
+       probe: sv_probe,
+       remove: sv_remove
+};
+ 
+static int __init init_sonicvibes(void)
+{
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.25 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.26 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
 #endif
-	while (index < NR_DEVICE && 
-	       (pcidev = pci_find_device(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, pcidev))) {
-		if (!RSRCISIOREGION(pcidev, RESOURCE_SB) ||
-		    !RSRCISIOREGION(pcidev, RESOURCE_ENH) ||
-		    !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) ||
-		    !RSRCISIOREGION(pcidev, RESOURCE_MIDI) ||
-		    !RSRCISIOREGION(pcidev, RESOURCE_GAME))
-			continue;
-		if (pcidev->irq == 0)
-			continue;
-		/* try to allocate a DDMA resource if not already available */
-		if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) {
-			pcidev->resource[RESOURCE_DDMA].start = 0;
-			pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1;
-			pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO;
-			ddmanamelen = strlen(sv_ddma_name)+1;
-			if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL)))
-				continue;
-			memcpy(ddmaname, sv_ddma_name, ddmanamelen);
-			pcidev->resource[RESOURCE_DDMA].name = ddmaname;
-			if (pci_assign_resource(pcidev, RESOURCE_DDMA)) {
-				pcidev->resource[RESOURCE_DDMA].name = NULL;
-				kfree(ddmaname);
-				printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n");
-				continue;
-			}
-		}
-		if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) {
-			printk(KERN_WARNING "sv: out of memory\n");
-			continue;
-		}
-		memset(s, 0, sizeof(struct sv_state));
-		init_waitqueue_head(&s->dma_adc.wait);
-		init_waitqueue_head(&s->dma_dac.wait);
-		init_waitqueue_head(&s->open_wait);
-		init_waitqueue_head(&s->midi.iwait);
-		init_waitqueue_head(&s->midi.owait);
-		init_MUTEX(&s->open_sem);
-		spin_lock_init(&s->lock);
-		s->magic = SV_MAGIC;
-		s->iosb = RSRCADDRESS(pcidev, RESOURCE_SB);
-		s->ioenh = RSRCADDRESS(pcidev, RESOURCE_ENH);
-		s->iosynth = RSRCADDRESS(pcidev, RESOURCE_SYNTH);
-		s->iomidi = RSRCADDRESS(pcidev, RESOURCE_MIDI);
-		s->iogame = RSRCADDRESS(pcidev, RESOURCE_GAME);
-		s->iodmaa = RSRCADDRESS(pcidev, RESOURCE_DDMA);
-		s->iodmac = RSRCADDRESS(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA;
-		pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9);  /* enable and use extended mode */
-		pci_write_config_dword(pcidev, 0x48, s->iodmac | 9);  /* enable */
-		printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#lx %#x %#x\n",
-		       s->iosb, s->ioenh, s->iosynth, s->iomidi, s->iogame, s->iodmaa, s->iodmac);
-		s->irq = pcidev->irq;
-
-		/* hack */
-		pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12);  /* wavetable base address */
-
-		if (check_region(s->ioenh, SV_EXTENT_ENH)) {
-			printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1);
-			goto err_region5;
-		}
-		request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM");
-		if (check_region(s->iodmaa, SV_EXTENT_DMA)) {
-			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1);
-			goto err_region4;
-		}
-		request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA");
-		if (check_region(s->iodmac, SV_EXTENT_DMA)) {
-			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1);
-			goto err_region3;
-		}
-		request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC");
-		if (check_region(s->iomidi, SV_EXTENT_MIDI)) {
-			printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1);
-			goto err_region2;
-		}
-		request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi");
-		if (check_region(s->iosynth, SV_EXTENT_SYNTH)) {
-			printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1);
-			goto err_region1;
-		}
-		request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth");
-		/* initialize codec registers */
-		outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */
-		udelay(50);
-		outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */
-		udelay(50);
-		outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */
-		     | (reverb[index] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL);
-		inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */
-		wrindir(s, SV_CIDRIVECONTROL, 0);  /* drive current 16mA */
-		wrindir(s, SV_CIENABLE, s->enable = 0);  /* disable DMAA and DMAC */
-		outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK);
-		/* outb(0xff, s->iodmaa + SV_DMA_RESET); */
-		/* outb(0xff, s->iodmac + SV_DMA_RESET); */
-		inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
-		wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */
-		wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */
-		wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */
-		setpll(s, SV_CIADCPLLM, 8000);
-		wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */
-		wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff);
-		wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff);
-		wrindir(s, SV_CIADCOUTPUT, 0);
-		/* request irq */
-		if (request_irq(s->irq, sv_interrupt, SA_SHIRQ, "S3 SonicVibes", s)) {
-			printk(KERN_ERR "sv: irq %u in use\n", s->irq);
-			goto err_irq;
-		}
-		printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n",
-		       s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
-		/* register devices */
-		if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0)
-			goto err_dev1;
-		if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0)
-			goto err_dev2;
-		if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0)
-			goto err_dev3;
-		if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0)
-			goto err_dev4;
-		pci_set_master(pcidev);  /* enable bus mastering */
-		/* initialize the chips */
-		fs = get_fs();
-		set_fs(KERNEL_DS);
-		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH;
-		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
-		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
-			val = initvol[i].vol;
-			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
-		}
-		set_fs(fs);
-		/* queue it for later freeing */
-		s->next = devs;
-		devs = s;
-		index++;
-		continue;
-
-	err_dev4:
-		unregister_sound_midi(s->dev_midi);
-	err_dev3:
-		unregister_sound_mixer(s->dev_mixer);
-	err_dev2:
-		unregister_sound_dsp(s->dev_audio);
-	err_dev1:
-		printk(KERN_ERR "sv: cannot register misc device\n");
-		free_irq(s->irq, s);
-	err_irq:
-		release_region(s->iosynth, SV_EXTENT_SYNTH);
-	err_region1:
-		release_region(s->iomidi, SV_EXTENT_MIDI);
-	err_region2:
-		release_region(s->iodmac, SV_EXTENT_DMA);
-	err_region3:
-		release_region(s->iodmaa, SV_EXTENT_DMA);
-	err_region4:
-		release_region(s->ioenh, SV_EXTENT_ENH);
-	err_region5:
-		kfree_s(s, sizeof(struct sv_state));
-	}
-	if (!devs) {
-		if (wavetable_mem)
-			free_pages(wavetable_mem, 20-PAGE_SHIFT);
-		return -ENODEV;
-	}
-	return 0;
+	if (!pci_register_driver(&sv_driver))
+                return -ENODEV;
+        return 0;
 }
 
 static void __exit cleanup_sonicvibes(void)
 {
-	struct sv_state *s;
-
-	while ((s = devs)) {
-		devs = devs->next;
-		outb(~0, s->ioenh + SV_CODEC_INTMASK);  /* disable ints */
-		synchronize_irq();
-		inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
-		wrindir(s, SV_CIENABLE, 0);     /* disable DMAA and DMAC */
-		/*outb(0, s->iodmaa + SV_DMA_RESET);*/
-		/*outb(0, s->iodmac + SV_DMA_RESET);*/
-		free_irq(s->irq, s);
-		release_region(s->iodmac, SV_EXTENT_DMA);
-		release_region(s->iodmaa, SV_EXTENT_DMA);
-		release_region(s->ioenh, SV_EXTENT_ENH);
-		release_region(s->iomidi, SV_EXTENT_MIDI);
-		release_region(s->iosynth, SV_EXTENT_SYNTH);
-		unregister_sound_dsp(s->dev_audio);
-		unregister_sound_mixer(s->dev_mixer);
-		unregister_sound_midi(s->dev_midi);
-		unregister_sound_special(s->dev_dmfm);
-		kfree_s(s, sizeof(struct sv_state));
-	}
-	if (wavetable_mem)
-		free_pages(wavetable_mem, 20-PAGE_SHIFT);
 	printk(KERN_INFO "sv: unloading\n");
+	pci_unregister_driver(&sv_driver);
+ 	if (wavetable_mem)
+		free_pages(wavetable_mem, 20-PAGE_SHIFT);
 }
 
 module_init(init_sonicvibes);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)