linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Updates to Maestro{1,2,2E} driver -- multiple open of dsp, persistent buffers.
@ 2001-07-06  9:16 Daniel Pittman
  2001-07-06 11:14 ` Alan Cox
  0 siblings, 1 reply; 2+ messages in thread
From: Daniel Pittman @ 2001-07-06  9:16 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 674 bytes --]

The attached patch modifies the kernel Maestro sound driver to add a
couple of features:

* open /dev/dsp many times, not /dev/dsp[0-3]
* persistent DMA buffers

This code has been well tested and has run for quite a long time with
stability and success on a number of Maestro-using computers.

The patch has the blessing of Zach Brown, the current maintainer, as far
as that goes -- but it's my own work. :)

It would be good if people could test this and let me know if they
encounter any problems. If not, I will submit it for inclusion in the
kernel proper soon.

The patch is against 2.4.7-pre3 but should apply successfully to version
2.4.6 or later.

        Daniel


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: maestro-2001-07-06-multi-open-2.4.7-pre1.diff --]
[-- Type: text/x-patch, Size: 33000 bytes --]

diff -ru linux/Documentation/Configure.help linux.maestro/Documentation/Configure.help
--- linux/Documentation/Configure.help	Fri Jul  6 17:15:55 2001
+++ linux.maestro/Documentation/Configure.help	Thu Jul  5 11:58:39 2001
@@ -15607,6 +15607,22 @@
   of PCI sound chips.  These include the Maestro 1, Maestro 2, and
   Maestro 2E.  See Documentation/sound/Maestro for more details.
 
+The number of DSP device to create.
+CONFIG_MAESTRO_CHANNELS
+  The number of DSP devices to allocate. Each device functions as
+  a fully independent sound system. The more devices allocated,
+  however, the larger the memory requirement for the driver.
+  You can allocate up to four DSP devices, but only in multiples
+  of two. This allows one, two or four devices.
+
+Allocate memory for DSP devices persistently
+CONFIG_MAESTRO_PERSIST
+  If you say Y to this, the memory for the DSP devices provided
+  by the Maestro device will be retained even when the sound
+  system is not in use.
+  This is beneficial if you find that sound cannot be used after
+  doing other things on the machine for a while.
+
 Are you using a crosscompiler
 CONFIG_CROSSCOMPILE
   Say Y here if you are compiling the kernel on a different
diff -ru linux/Documentation/sound/Maestro linux.maestro/Documentation/sound/Maestro
--- linux/Documentation/sound/Maestro	Sat Jul 29 05:50:52 2000
+++ linux.maestro/Documentation/sound/Maestro	Thu Jul  5 11:58:22 2001
@@ -80,16 +80,32 @@
 an IRQ.  This operation is incredibly system specific, so you're on your
 own.  Sometimes the magic lies in 'PNP Capable Operating System' settings.
 
-There are very few options to the driver.  One is 'debug' which will 
-tell the driver to print minimal debugging information as it runs.  This
-can be collected with 'dmesg' or through the klogd daemon.
-
-The other, more interesting option, is 'dsps_order'.  Typically at
-install time the driver will only register one available /dev/dsp device
-for its use.  The 'dsps_order' module parameter allows for more devices
-to be allocated, as a power of two.  Up to 4 devices can be registered
-( dsps_order=2 ).  These devices act as fully distinct units and use
-separate channels in the maestro.
+There are very few options to the driver. Options can be specified as either
+module options or on the kernel command line. Command line arguments are in
+the form 'maestro=<option>:<value>,<option>:<value>'.
+
+One is 'debug' which will tell the driver to print minimal debugging
+information as it runs. This can be collected with 'dmesg' or through the
+klogd daemon. Setting this to one will cause debug information to be dumped.
+
+The other, more interesting option, is 'channels'. At install time the driver
+will register /dev/dsp and makes a number of channels available through this
+device. Multiple programs can open /dev/dsp, until all channels are used.
+
+The 'channels' parameter specifies the number of channels to be specified. Up
+to 4 channels can be allocated, but only in powers of two. Thus, you can have
+one, two or four devices. The default value is to allocate one channel.
+
+When multiple channels are open, they act as distinct sound devices. This
+allows multiple applications to generate sound simultaneously without blocking
+each other.
+
+At compile time you may also select to use persistent dsp buffers. Because of
+some oddities of the hardware, a large number of channels requires a large
+slab of contiguous memory. The persistent buffers option allocates this memory
+when the cards are detected and holds it all the time, preventing memory
+fragmentation from preventing the sound device from working.
+
 
 Power Management
 ----------------
diff -ru linux/drivers/sound/Config.in linux.maestro/drivers/sound/Config.in
--- linux/drivers/sound/Config.in	Fri Jul  6 17:16:21 2001
+++ linux.maestro/drivers/sound/Config.in	Thu Jul  5 14:15:43 2001
@@ -36,6 +36,10 @@
 dep_tristate '  Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND $CONFIG_PCI
 dep_tristate '  ESS Technology Solo1' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND
 dep_tristate '  ESS Maestro, Maestro2, Maestro2E driver' CONFIG_SOUND_MAESTRO $CONFIG_SOUND
+if [ "$CONFIG_SOUND_MAESTRO" = "y" -o "$CONFIG_SOUND_MAESTRO" = "m" ]; then
+   int  '    Number of channels (1,2,4)' CONFIG_MAESTRO_CHANNELS 1
+   bool '    Persistent Maestro dsp buffers' CONFIG_MAESTRO_PERSIST
+fi
 dep_tristate '  ESS Maestro3/Allegro driver (EXPERIMENTAL)' CONFIG_SOUND_MAESTRO3 $CONFIG_SOUND $CONFIG_PCI $CONFIG_EXPERIMENTAL
 dep_tristate '  Intel ICH (i8xx) audio support' CONFIG_SOUND_ICH $CONFIG_PCI
 dep_tristate '  S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND
diff -ru linux/drivers/sound/maestro.c linux.maestro/drivers/sound/maestro.c
--- linux/drivers/sound/maestro.c	Fri Jul  6 17:16:21 2001
+++ linux.maestro/drivers/sound/maestro.c	Thu Jul  5 15:42:42 2001
@@ -1,6 +1,6 @@
 /*****************************************************************************
  *
- *      ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x
+ *      ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[234].x
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
  *	proprietors of Hack Central for fine lodging.
  *
  *  Supported devices:
- *  /dev/dsp0-3    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
  *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
  *
  *  Hardware Description
@@ -39,7 +39,7 @@
  *	channels.  They can take data from a number of sources and perform
  *	basic encodings of the data.  The wavecache is a storehouse for
  *	PCM data.  Typically it deals with PCI and interracts with the
- *	APUs.  The ASSP is a wacky DSP like device that ESS is loth
+ *	APUs.  The ASSP is a wacky DSP like device that ESS is loath
  *	to release docs on.  Thankfully it isn't required on the Maestro
  *	until you start doing insane things like FM emulation and surround
  *	encoding.  The codecs are almost always AC-97 compliant codecs, 
@@ -69,20 +69,19 @@
  *	software.  The pass between the 2 APUs is supposedly what requires us
  *	to have a 512 byte buffer sitting around in wavecache/memory.
  *
- *	The wavecache makes our life even more fun.  First off, it can
- *	only address the first 28 bits of PCI address space, making it
- *	useless on quite a few architectures.  Secondly, its insane.
- *	It claims to fetch from 4 regions of PCI space, each 4 meg in length.
- *	But that doesn't really work.  You can only use 1 region.  So all our
- *	allocations have to be in 4meg of each other.  Booo.  Hiss.
- *	So we have a module parameter, dsps_order, that is the order of
- *	the number of dsps to provide.  All their buffer space is allocated
- *	on open time.  The sonicvibes OSS routines we inherited really want
- *	power of 2 buffers, so we have all those next to each other, then
- *	512 byte regions for the recording wavecaches.  This ends up
- *	wasting quite a bit of memory.  The only fixes I can see would be 
- *	getting a kernel allocator that could work in zones, or figuring out
- *	just how to coerce the WP into doing what we want.
+ *      The wavecache makes our life even more fun. First off, it can only address
+ *      the first 28 bits of PCI address space, making it useless on quite a
+ *      few architectures. Secondly, its insane. It claims to fetch from 4
+ *      regions of PCI space, each 4 meg in length. But that doesn't really
+ *      work. You can only use 1 region. So all our allocations have to be in
+ *      4meg of each other. Booo. Hiss. So we have a module parameter,
+ *      channels, that is the number of dsps to provide. All their buffer
+ *      space is allocated on open time. The sonicvibes OSS routines we
+ *      inherited really want power of 2 buffers, so we have all those next to
+ *      each other, then 512 byte regions for the recording wavecaches. This
+ *      ends up wasting quite a bit of memory. The only fixes I can see would
+ *      be getting a kernel allocator that could work in zones, or figuring
+ *      out just how to coerce the WP into doing what we want.
  *
  *	The indirection of the various registers means we have to spinlock
  *	nearly all register accesses.  We have the main register indirection
@@ -115,6 +114,15 @@
  *	themselves, but we'll see.  
  *	
  * History
+ *  v0.15 - Jul 05 2001 - Daniel Pittman <daniel@rimspace.net>
+ *      Support multiple open of /dev/dsp, not multiple dsp devices.
+ *      Parse the kernel command line for arguments.
+ *      Add module parameter descriptions.
+ *      Added configure time support for setting default number of dsp
+ *      channels to use. This is not the dsps_order value, because that's
+ *      totally, like, impossible to explain to anyone who isn't a math geek.
+ *      Added support for persistent sound buffer allocation for a card so
+ *      that my laptop will still be able to play sound after a compile.
  *  v0.15 - May 21 2001 - Marcus Meissner <mm@caldera.de>
  *      Ported to Linux 2.4 PCI API. Some clean ups, global devs list
  *      removed (now using pci device driver data).
@@ -246,26 +254,48 @@
 #define M_printk(x)
 #endif
 
-/* we try to setup 2^(dsps_order) /dev/dsp devices */
-static int dsps_order=0;
+/* Number of dsp channels to allocate. (1,2 or 4) */
+static int channels = CONFIG_MAESTRO_CHANNELS;
+
+/* Should we allocate persistent DMA buffers? */
+#ifdef CONFIG_MAESTRO_PERSIST
+static int persist = 1;
+#else
+static int persist = 0;
+#endif
+
 /* wether or not we mess around with power management */
 static int use_pm=2; /* set to 1 for force */
+
 /* clocking for broken hardware - a few laptops seem to use a 50Khz clock
 	ie insmod with clocking=50000 or so */
-	
 static int clocking=48000;
 
+/* --------------------------------------------------------------------- */
+#define DRIVER_VERSION "0.16"
+
+#ifdef MODULE
 MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Alan Cox <alan@redhat.com>");
 MODULE_DESCRIPTION("ESS Maestro Driver");
 #ifdef M_DEBUG
 MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"Extra debug information from the driver.");
 #endif
-MODULE_PARM(dsps_order,"i");
+
+MODULE_PARM(channels,"i");
+MODULE_PARM_DESC(channels,"Number of channels to support on /dev/dsp.");
+
 MODULE_PARM(use_pm,"i");
-MODULE_PARM(clocking, "i");
+MODULE_PARM_DESC(use_pm,"Use power management? (0 to disable, 1 to try, 2 to force).");
 
-/* --------------------------------------------------------------------- */
-#define DRIVER_VERSION "0.15"
+MODULE_PARM(persist,"i");
+MODULE_PARM_DESC(persist,"Allocate persistent DMA buffers? (0 to disable, 1 to force)");
+
+MODULE_PARM(clocking, "i");
+MODULE_PARM_DESC(clocking,"Set the clocking for broken hardware.
+A few laptops seem to use a 50Khz clock.
+ie insmod with clocking=50000 or so.");
+#endif /* MODULE */
 
 #ifndef PCI_VENDOR_ESS
 #define PCI_VENDOR_ESS			0x125D
@@ -300,8 +330,9 @@
 #define ADC_RUNNING		2
 
 #define MAX_DSP_ORDER	2
+#define DSP_ORDER	(channels/2)
 #define MAX_DSPS	(1<<MAX_DSP_ORDER)
-#define NR_DSPS		(1<<dsps_order)
+#define NR_DSPS		(1<<DSP_ORDER)
 #define NR_IDRS		32
 
 #define NR_APUS		64
@@ -395,13 +426,9 @@
 
 	/* this locks around the oss state in the driver */
 	spinlock_t lock;
-	/* only let 1 be opening at a time */
-	struct semaphore open_sem;
-	wait_queue_head_t open_wait;
-	mode_t open_mode;
 
-	/* soundcore stuff */
-	int dev_audio;
+	/* Current open modes for the channel. */
+	mode_t open_mode;
 
 	struct dmabuf {
 		void *rawbuf;
@@ -440,6 +467,8 @@
 	/* We keep maestro cards in a linked list */
 	struct ess_card *next;
 
+	/* soundcore stuff */
+	int dev_audio;
 	int dev_mixer;
 
 	int card_type;
@@ -463,6 +492,13 @@
 	int in_suspend;
 	wait_queue_head_t suspend_queue;
 
+	/* Lock to protect the channel list during open. */
+	struct semaphore open_semaphore;
+
+	/* Queue for sleepers waiting for a free channel. */
+	wait_queue_head_t open_queue;
+
+	int channel_count;
 	struct ess_state channels[MAX_DSPS];
 	u16 maestro_map[NR_IDRS];	/* Register map */
 	/* we have to store this junk so that we can come back from a
@@ -787,7 +823,7 @@
 	
 /* read or write the recmask 
 	the ac97 can really have left and right recording
-	inputs independantly set, but OSS doesn't seem to 
+	inputs independently set, but OSS doesn't seem to 
 	want us to express that to the user. 
 	the caller guarantees that we have a supported bit set,
 	and they must be holding the card's spinlock */
@@ -842,7 +878,7 @@
 		vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf);
 
 	if (! (caps & 0x4) ) {
-		/* no bass/treble nobs */
+		/* no bass/treble knobs */
 		card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
 	}
 
@@ -1454,7 +1490,7 @@
  *	Native record driver 
  */
 
-/* again, passed mode is alrady shifted/masked */
+/* again, passed mode is already shifted/masked */
 static void 
 ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size)
 {
@@ -1512,7 +1548,7 @@
 				pa = virt_to_bus(buffer);
 			} else {
 				/* right channel records its split half.
-				*2 accomodates for rampant shifting earlier */
+				*2 accommodates for rampant shifting earlier */
 				pa = virt_to_bus(buffer + size*2);
 			}
 
@@ -1653,7 +1689,7 @@
 		if(freq > (ESS_SYSCLK>>(prescale+9)))
 			break;
 			
-	/* next, back off prescaler whilst getting divider into optimum range */
+	/* next, back off prescaler while getting divider into optimum range */
 	divide=1;
 	while((prescale > 5) && (divide<32))
 	{
@@ -1822,10 +1858,11 @@
 
 	/* update ADC pointer */
 	if (s->dma_adc.ready) {
-		/* oh boy should this all be re-written.  everything in the current code paths think
-		that the various counters/pointers are expressed in bytes to the user but we have
-		two apus doing stereo stuff so we fix it up here.. it propogates to all the various
-		counters from here.  */
+		/* oh boy should this all be re-written. everything in the
+		current code paths think that the various counters/pointers
+		are expressed in bytes to the user but we have two apus doing
+		stereo stuff so we fix it up here.. it propagates to all the
+		various counters from here. */
 		if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
 			hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize;
 		} else {
@@ -1958,11 +1995,10 @@
 	/*
 	 *	Update the pointers for all APU's we are running.
 	 */
-	for(i=0;i<NR_DSPS;i++)
+	for(i = 0; i < c->channel_count; i++)
 	{
 		s=&c->channels[i];
-		if(s->dev_audio == -1)
-			break;
+
 		spin_lock(&s->lock);
 		ess_update_ptr(s);
 		spin_unlock(&s->lock);
@@ -2893,14 +2929,14 @@
 	|silly fifo word	| 512byte mixbuf per adc	| dac/adc * channels |
 */
 static int
-allocate_buffers(struct ess_state *s)
+allocate_buffers(struct ess_card *c)
 {
 	void *rawbuf=NULL;
 	int order,i;
 	struct page *page, *pend;
 
 	/* alloc as big a chunk as we can */
-	for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--)
+	for (order = (DSP_ORDER + (16-PAGE_SHIFT) + 1); order >= (DSP_ORDER + 2 + 1); order--)
 		if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
 			break;
 
@@ -2916,18 +2952,15 @@
 		return 1;
 	}
 
-	s->card->dmapages = rawbuf;
-	s->card->dmaorder = order;
+	c->dmapages = rawbuf;
+	c->dmaorder = order;
 
-	for(i=0;i<NR_DSPS;i++) {
-		struct ess_state *ess = &s->card->channels[i];
+	for(i = 0; i < c->channel_count; i++) {
+		struct ess_state *ess = &c->channels[i];
 
-		if(ess->dev_audio == -1)
-			continue;
-
-		ess->dma_dac.ready = s->dma_dac.mapped = 0;
-		ess->dma_adc.ready = s->dma_adc.mapped = 0;
-		ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1;
+		ess->dma_dac.ready = 0;
+		ess->dma_adc.ready = 0;
+		ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - DSP_ORDER - 1;
 
 		/* offset dac and adc buffers starting half way through and then at each [da][ad]c's
 			order's intervals.. */
@@ -2950,105 +2983,104 @@
 	return 0;
 } 
 static void
-free_buffers(struct ess_state *s)
+free_buffers(struct ess_card *c)
 {
 	struct page *page, *pend;
 
-	s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL;
-	s->dma_dac.mapped = s->dma_adc.mapped = 0;
-	s->dma_dac.ready = s->dma_adc.ready = 0;
+	M_printk("maestro: freeing %p\n",c->dmapages);
 
-	M_printk("maestro: freeing %p\n",s->card->dmapages);
 	/* undo marking the pages as reserved */
-
-	pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1);
-	for (page = virt_to_page(s->card->dmapages); page <= pend; page++)
+	pend = virt_to_page(c->dmapages + (PAGE_SIZE << c->dmaorder) - 1);
+	for (page = virt_to_page(c->dmapages); page <= pend; page++)
 		mem_map_unreserve(page);
 
-	free_pages((unsigned long)s->card->dmapages,s->card->dmaorder);
-	s->card->dmapages = NULL;
+	free_pages((unsigned long)c->dmapages,c->dmaorder);
+	c->dmapages = NULL;
 }
 
 static int 
 ess_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	struct ess_card *card = NULL;
 	struct ess_state *s = NULL;
+ 	struct pci_dev *pdev = NULL;
+	int i;
 	unsigned char fmtm = ~0, fmts = 0;
-	struct pci_dev *pdev;
-	/*
-	 *	Scan the cards and find the channel. We only
-	 *	do this at open time so it is ok
-	 */
 
-	pci_for_each_dev(pdev) {
-		struct ess_card *c;
-		struct pci_driver *drvr;
-
-		drvr = pci_dev_driver (pdev);
-		if (drvr == &maestro_pci_driver) {
-			int i;
-			struct ess_state *sp;
+ 	pci_for_each_dev(pdev) {
+ 		struct pci_driver *drvr;
+ 
+ 		drvr = pci_dev_driver (pdev);
+ 		if (drvr == &maestro_pci_driver) {
+ 			card = (struct ess_card*)pci_get_drvdata (pdev);
+ 			if (!card)
+ 				continue;
+			if (card->dev_audio == minor)
+				break;
+  		}
+  	}
 
-			c = (struct ess_card*)pci_get_drvdata (pdev);
-			if (!c)
-				continue;
-			for(i=0;i<NR_DSPS;i++)
-			{
-				sp=&c->channels[i];
-				if(sp->dev_audio < 0)
-					continue;
-				if((sp->dev_audio ^ minor) & ~0xf)
-					continue;
-				s=sp;
-			}
-		}
-	}
-	if (!s)
+	/* Found the card the user wanted? */
+	if (card == NULL)
 		return -ENODEV;
+	
+	/* Find the card that the user opened. */
+	while (s == NULL)
+	{
+		/* Lock the list of channels. */
+		down(&card->open_semaphore);
+	
+		/* Scan the card and find an available channel. */
+		for(i = 0; i < card->channel_count && s == NULL; i++)
+		{
+			if(card->channels[i].open_mode != 0)
+				continue; /* busy */
+			
+			s = &card->channels[i];
+		}
 
-       	VALIDATE_STATE(s);
-	file->private_data = s;
-	/* wait for device to become free */
-	down(&s->open_sem);
-	while (s->open_mode & file->f_mode) {
-		if (file->f_flags & O_NONBLOCK) {
-			up(&s->open_sem);
+		if (s != NULL)
+			break;
+		
+		/* Unlock this card. */
+		up(&card->open_semaphore);
+		if (file->f_flags & O_NONBLOCK)
 			return -EWOULDBLOCK;
-		}
-		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+	
+		/* Sleep until something happens. */
+		interruptible_sleep_on(&card->open_queue);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
-		down(&s->open_sem);
 	}
+		
+	/* Found an available channel. */
+       	VALIDATE_STATE(s);
+	file->private_data = s;
 
-	/* under semaphore.. */
-	if ((s->card->dmapages==NULL) && allocate_buffers(s)) {
-		up(&s->open_sem);
-		return -ENOMEM;
+	if (!persist)
+	{
+		if ((card->dmapages==NULL) && allocate_buffers(card)) {
+			up(&card->open_semaphore);
+			return -ENOMEM;
+		}
 	}
 
 	/* we're covered by the open_sem */
-	if( ! s->card->dsps_open )  {
-		maestro_power(s->card,ACPI_D0);
+	if( ! card->dsps_open )  {
+		maestro_power(card,ACPI_D0);
 		start_bob(s);
 	}
-	s->card->dsps_open++;
-	M_printk("maestro: open, %d bobs now\n",s->card->dsps_open);
+	card->dsps_open++;
+	M_printk("maestro: open, %d bobs now\n",card->dsps_open);
 
 	/* ok, lets write WC base regs now that we've 
-		powered up the chip */
-	M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages),
-		((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12);
-	set_base_registers(s,s->card->dmapages);
+	   powered up the chip */
+	M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(card->dmapages),
+		 ((virt_to_bus(card->dmapages))&0xFFE00000)>>12);
+	set_base_registers(s,card->dmapages);
 
 	if (file->f_mode & FMODE_READ) {
-/*
-		fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
-		if ((minor & 0xf) == SND_DEV_DSP16)
-			fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */
-
 		fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT);
 		fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT;
 
@@ -3066,7 +3098,7 @@
 	set_fmt(s, fmtm, fmts);
 	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
 
-	up(&s->open_sem);
+	up(&card->open_semaphore);
 	return 0;
 }
 
@@ -3074,12 +3106,14 @@
 ess_release(struct inode *inode, struct file *file)
 {
 	struct ess_state *s = (struct ess_state *)file->private_data;
+	struct ess_card  *card = s->card;
 
 	VALIDATE_STATE(s);
-	lock_kernel();
+	lock_kernel();			/* need this? --daniel */
 	if (file->f_mode & FMODE_WRITE)
 		drain_dac(s, file->f_flags & O_NONBLOCK);
-	down(&s->open_sem);
+
+	down(&card->open_semaphore);
 	if (file->f_mode & FMODE_WRITE) {
 		stop_dac(s);
 	}
@@ -3089,15 +3123,18 @@
 		
 	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
 	/* we're covered by the open_sem */
-	M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1);
-	if( --s->card->dsps_open <= 0) {
-		s->card->dsps_open = 0;
+	M_printk("maestro: %d dsps now alive\n",card->dsps_open-1);
+	if( --card->dsps_open <= 0) {
+		card->dsps_open = 0;
 		stop_bob(s);
-		free_buffers(s);
-		maestro_power(s->card,ACPI_D2);
+		if (!persist)
+			free_buffers(card);
+		maestro_power(card,ACPI_D2);
 	}
-	up(&s->open_sem);
-	wake_up(&s->open_wait);
+
+	/* Tell callers they can find an open channel. */
+	up(&card->open_semaphore);
+	wake_up(&card->open_queue);
 	unlock_kernel();
 	return 0;
 }
@@ -3384,14 +3421,13 @@
 static int __init
 maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid)
 {
-	int card_type = pdid->driver_data;
+ 	int card_type = pdid->driver_data;
 	u32 n;
 	int iobase;
-	int i, ret;
+ 	int i, ret;
 	struct ess_card *card;
 	struct ess_state *ess;
 	struct pm_dev *pmdev;
-	int num = 0;
 
 /* when built into the kernel, we only print version if device is found */
 #ifndef MODULE
@@ -3402,24 +3438,23 @@
 
 	/* don't pick up weird modem maestros */
 	if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
-		return -ENODEV;
-
-
-	if ((ret=pci_enable_device(pcidev)))
-		return ret;
-			
-	iobase = pci_resource_start(pcidev,0);
-	if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO))
-		return -ENODEV;
-
-	if(pcidev->irq == 0)
-		return -ENODEV;
+ 		return -ENODEV;
+ 
+ 	if ((ret=pci_enable_device(pcidev)))
+ 		return ret;
+  			
+ 	iobase = pci_resource_start(pcidev,0);
+ 	if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO))
+ 		return -ENODEV;
+ 
+ 	if(pcidev->irq == 0)
+ 		return -ENODEV;
 
 	/* stake our claim on the iospace */
 	if( request_region(iobase, 256, card_names[card_type]) == NULL )
 	{
 		printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase);
-		return -EBUSY;
+ 		return -EBUSY;
 	}
 
 	/* just to be sure */
@@ -3437,18 +3472,29 @@
 	card->pcidev = pcidev;
 
 	pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev),
-			maestro_pm_callback);
+			    maestro_pm_callback);
 	if (pmdev)
 		pmdev->data = card;
 
+	/* Allocate the DSP channel. */
+	if ((card->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
+	{
+		kfree(card);
+		printk(KERN_WARNING "maestro: unable to allocate DSP device.\n");
+		return -ENOMEM;
+	}
+	
 	card->iobase = iobase;
 	card->card_type = card_type;
 	card->irq = pcidev->irq;
 	card->magic = ESS_CARD_MAGIC;
+ 	card->dock_mute_vol = 50;
 	spin_lock_init(&card->lock);
 	init_waitqueue_head(&card->suspend_queue);
+ 
 
-	card->dock_mute_vol = 50;
+	init_waitqueue_head(&card->open_queue);
+	init_MUTEX(&card->open_semaphore);
 	
 	/* init our groups of 6 apus */
 	for(i=0;i<NR_DSPS;i++)
@@ -3460,9 +3506,7 @@
 		s->card = card;
 		init_waitqueue_head(&s->dma_adc.wait);
 		init_waitqueue_head(&s->dma_dac.wait);
-		init_waitqueue_head(&s->open_wait);
 		spin_lock_init(&s->lock);
-		init_MUTEX(&s->open_sem);
 		s->magic = ESS_STATE_MAGIC;
 		
 		s->apu[0] = 6*i;
@@ -3473,44 +3517,39 @@
 		s->apu[5] = (6*i)+5;
 		
 		if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
-			printk("maestro: BOTCH!\n");
+			printk(KERN_WARNING "maestro: Failed to init card DAC correctly!\n");
 		/* register devices */
-		if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
-			break;
-	}
-	
-	num = i;
-	
-	/* clear the rest if we ran out of slots to register */
-	for(;i<NR_DSPS;i++)
-	{
-		struct ess_state *s=&card->channels[i];
-		s->dev_audio = -1;
 	}
+
+	/* Remember the number of channels allocated on this card. */
+	card->channel_count = i;
+
+	/* REVISIT: 'num' becomes 'card->channel_count'  */
 	
 	ess = &card->channels[0];
 
+
 	/*
 	 *	Ok card ready. Begin setup proper
 	 */
 
 	printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", 
-		card_names[card_type],iobase,card->irq);
+	       card_names[card_type],iobase,card->irq);
 	pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n);
 	printk(KERN_INFO "maestro:  subvendor id: 0x%08x\n",n); 
 
 	/* turn off power management unless:
 	 *	- the user explicitly asks for it
 	 * 		or
-	 *		- we're not a 2e, lesser chipps seem to have problems.
-	 *		- we're not on our _very_ small whitelist.  some implemenetations
-	 *			really dont' like the pm code, others require it.
+	 *		- we're not a 2e, lesser chips seem to have problems.
+	 *		- we're not on our _very_ small whitelist.  some implementations
+	 *			really don't like the pm code, others require it.
 	 *			feel free to expand this as required.
 	 */
 #define SUBSYSTEM_VENDOR(x) (x&0xffff)
 	if(	(use_pm != 1) && 
 		((card_type != TYPE_MAESTRO2E)	|| (SUBSYSTEM_VENDOR(n) != 0x1028)))
-			use_pm = 0;
+		use_pm = 0;
 
 	if(!use_pm) 
 		printk(KERN_INFO "maestro: not attempting power management.\n");
@@ -3527,7 +3566,7 @@
 
 	if(maestro_ac97_get(card, 0x00)==0x0080) {
 		printk(KERN_ERR "maestro: my goodness!  you seem to have a pt101 codec, which is quite rare.\n"
-				"\tyou should tell someone about this.\n");
+		       "\tyou should tell someone about this.\n");
 	} else {
 		maestro_ac97_init(card);
 	}
@@ -3541,50 +3580,62 @@
 	
 	if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card)))
 	{
-		printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq);
+		printk(KERN_ERR "maestro: unable to allocate irq %d.\n", card->irq);
 		unregister_sound_mixer(card->dev_mixer);
-		for(i=0;i<NR_DSPS;i++)
-		{
-			struct ess_state *s = &card->channels[i];
-			if(s->dev_audio != -1)
-				unregister_sound_dsp(s->dev_audio);
-		}
+		unregister_sound_dsp(card->dev_audio);
 		release_region(card->iobase, 256);		
 		unregister_reboot_notifier(&maestro_nb);
 		kfree(card);
 		return ret;
 	}
 
-	/* Turn on hardware volume control interrupt.
-	   This has to come after we grab the IRQ above,
-	   or a crash will result on installation if a button has been pressed,
-	   because in that case we'll get an immediate interrupt. */
-	n = inw(iobase+0x18);
-	n|=(1<<6);
-	outw(n, iobase+0x18);
-
-	pci_set_drvdata(pcidev,card);
+	/* Allocate buffers if we are doing them persistently. */
+	if (persist)
+	{
+		if (allocate_buffers(card))
+		{
+			printk(KERN_ERR "maestro: unable to allocate dsp buffer.\n");
+			unregister_sound_mixer(card->dev_mixer);
+			unregister_sound_dsp(card->dev_audio);
+			release_region(card->iobase, 256);		
+			unregister_reboot_notifier(&maestro_nb);
+			kfree(card);
+			return -ENOMEM;
+		}
+		printk(KERN_INFO "maestro: persistent dsp buffer allocated.\n");
+	}
+ 
+ 	/* Turn on hardware volume control interrupt.
+ 	   This has to come after we grab the IRQ above,
+ 	   or a crash will result on installation if a button has been pressed,
+ 	   because in that case we'll get an immediate interrupt. */
+ 	n = inw(iobase+0x18);
+ 	n|=(1<<6);
+ 	outw(n, iobase+0x18);
+ 
+ 	pci_set_drvdata(pcidev,card);
+	
 	/* now go to sleep 'till something interesting happens */
 	maestro_power(card,ACPI_D2);
 
-	printk(KERN_INFO "maestro: %d channels configured.\n", num);
-	return 0;
+	printk(KERN_INFO "maestro: %d channels configured.\n", card->channel_count);
+	return 0; 
 }
 
 static void maestro_remove(struct pci_dev *pcidev) {
 	struct ess_card *card = pci_get_drvdata(pcidev);
-	int i;
 	
 	/* XXX maybe should force stop bob, but should be all 
 		stopped by _release by now */
+
+	/* Release any persistent buffer from the card. */
+	if (persist)
+		free_buffers(card);
+ 
 	free_irq(card->irq, card);
 	unregister_sound_mixer(card->dev_mixer);
-	for(i=0;i<NR_DSPS;i++)
-	{
-		struct ess_state *ess = &card->channels[i];
-		if(ess->dev_audio != -1)
-			unregister_sound_dsp(ess->dev_audio);
-	}
+	unregister_sound_dsp(card->dev_audio);
+
 	/* Goodbye, Mr. Bond. */
 	maestro_power(card,ACPI_D3);
  	release_region(card->iobase, 256);
@@ -3615,14 +3666,22 @@
 #ifdef MODULE
 	printk(version);
 #endif
-	if (dsps_order < 0)   {
-		dsps_order = 1;
-		printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
-	}
-	else if (dsps_order > MAX_DSP_ORDER)  {
-		dsps_order = MAX_DSP_ORDER;
-		printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
+	if (channels < 0)   {
+		channels = 1;
+		printk(KERN_WARNING "maestro: minimum channel count 1.\n");
+	}
+	else if (channels > MAX_DSPS)  {
+		channels = MAX_DSPS;
+		printk(KERN_WARNING "maestro: maximum channel count %d.\n",MAX_DSPS);
+	}
+
+	/* Make sure we have a sensible order value. */
+	if (channels != NR_DSPS)
+	{
+		channels = NR_DSPS;
+		printk(KERN_WARNING "maestro: clipping channel count to %d\n",channels);
 	}
+
 	return 0;
 }
 
@@ -3630,22 +3689,58 @@
 {
 	/* this notifier is called when the kernel is really shut down. */
 	M_printk("maestro: shutting down\n");
-	/* this will remove all card instances too */
-	pci_unregister_driver(&maestro_pci_driver);
-	/* XXX dunno about power management */
+ 	/* this will remove all card instances too */
+ 	pci_unregister_driver(&maestro_pci_driver);
+ 	/* XXX dunno about power management */
 	return NOTIFY_OK;
 }
 
 /* --------------------------------------------------------------------- */
 
-
 void cleanup_maestro(void) {
 	M_printk("maestro: unloading\n");
-	pci_unregister_driver(&maestro_pci_driver);
-	pm_unregister_all(maestro_pm_callback);
-	unregister_reboot_notifier(&maestro_nb);
+ 	pci_unregister_driver(&maestro_pci_driver);
+ 	pm_unregister_all(maestro_pm_callback);
+ 	unregister_reboot_notifier(&maestro_nb);
+}
+
+
+#if !defined(MODULE)
+
+/* Process our command line arguments. */
+static int __init setup_maestro(char *str)
+{
+	char *option;
+	
+	if (!str || !*str)
+		return 0;
+
+	/* Read out our options. */
+	for (option = strtok(str, ",");
+	     option != NULL;
+	     option = strtok(NULL, ",")
+	)
+	{
+		if (!strncmp(option, "channels:", 9))
+			channels = simple_strtoul(option + 9, NULL, 0);
+		else if (!strncmp(option, "use_pm:", 7))
+			use_pm = simple_strtoul(option + 7, NULL, 0);
+		else if (!strncmp(option, "persist:", 8))
+			persist = simple_strtoul(option + 8, NULL, 0);
+		else if (!strncmp(option, "clocking:", 9))
+			clocking = simple_strtoul(option + 9, NULL, 0);
+#ifdef M_DEBUG		
+		else if (!strncmp(option, "debug:", 6))
+			debug = simple_strtoul(option + 6, NULL, 0);
+#endif /* M_DEBUG */
+	}
+
+	return 1;
 }
 
+__setup("maestro=", setup_maestro);
+#endif
+
 /* --------------------------------------------------------------------- */
 
 void
@@ -3678,18 +3773,14 @@
 		to power it up */
 	maestro_power(card,ACPI_D0);
 
-	for(i=0;i<NR_DSPS;i++) {
+	for(i=0;i<card->channel_count;i++) {
 		struct ess_state *s = &card->channels[i];
 
-		if(s->dev_audio == -1)
-			continue;
-
 		M_printk("maestro: stopping apus for device %d\n",i);
 		stop_dac(s);
 		stop_adc(s);
 		for(j=0;j<6;j++) 
 			card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5);
-
 	}
 
 	/* get rid of interrupts? */
@@ -3730,13 +3821,10 @@
 	/* set each channels' apu control registers before
 	 * restoring audio 
 	 */
-	for(i=0;i<NR_DSPS;i++) {
+	for(i=0;i<card->channel_count;i++) {
 		struct ess_state *s = &card->channels[i];
 		int chan,reg;
 
-		if(s->dev_audio == -1)
-			continue;
-
 		for(chan = 0 ; chan < 6 ; chan++) {
 			wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]);
 			for(reg = 1 ; reg < NR_APU_REGS ; reg++)  
@@ -3756,7 +3844,7 @@
 			this card */
 		maestro_power(card,ACPI_D0);
 		start_bob(&card->channels[0]);
-		for(i=0;i<NR_DSPS;i++) {
+		for(i = 0;i < card->channel_count; i++) {
 			struct ess_state *s = &card->channels[i];
 
 			/* these use the apu_mode, and can handle

[-- Attachment #3: Type: text/plain, Size: 86 bytes --]


-- 
Sweet is pleasure after pain.
        -- John Dryden,  _Alexander's Feast_, 1697

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] Updates to Maestro{1,2,2E} driver -- multiple open of dsp, persistent buffers.
  2001-07-06  9:16 [PATCH] Updates to Maestro{1,2,2E} driver -- multiple open of dsp, persistent buffers Daniel Pittman
@ 2001-07-06 11:14 ` Alan Cox
  0 siblings, 0 replies; 2+ messages in thread
From: Alan Cox @ 2001-07-06 11:14 UTC (permalink / raw)
  To: Daniel Pittman; +Cc: linux-kernel

> The patch has the blessing of Zach Brown, the current maintainer, as far
> as that goes -- but it's my own work. :)

You need to keep the dsp_order option as people have stuff relying on it
(at least for 2.4). It doesnt need to be documented or the recommended way
(and I agree its a bit strange) but it needs to work


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2001-07-06 11:14 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-07-06  9:16 [PATCH] Updates to Maestro{1,2,2E} driver -- multiple open of dsp, persistent buffers Daniel Pittman
2001-07-06 11:14 ` Alan Cox

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).