linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: i810_audio
       [not found] <3C3BFF98.9080309@redhat.com>
@ 2002-01-09  9:08 ` willy tarreau
  0 siblings, 0 replies; 35+ messages in thread
From: willy tarreau @ 2002-01-09  9:08 UTC (permalink / raw)
  To: Doug Ledford; +Cc: linux-kernel

> That's OK.  I found a pretty reliable way of causing
> it here.  There version of the driver on my web site
(now
> up to 0.18) no longer does anything like  that at
all here.

Hi Doug,

Congratulations! this works pretty well now. I'd like
to
thank you for this serious work.

Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Courrier : http://courrier.yahoo.fr

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

* Re: i810 audio
  2002-11-13  0:43         ` Alan Cox
@ 2002-11-13 12:08           ` Brian C. Huffman
  0 siblings, 0 replies; 35+ messages in thread
From: Brian C. Huffman @ 2002-11-13 12:08 UTC (permalink / raw)
  To: Alan Cox; +Cc: Peter Kundrat, linux-kernel

At least for mine, here's what I get out of /var/log/messages:

Nov 13 00:37:17 xxxxx kernel: i810_audio: Connection 0 with codec id 2
Nov 13 00:37:17 xxxxx kernel: ac97_codec: AC97 Audio codec, id:
ADS114(Unknown)
Nov 13 00:37:17 xxxxx kernel: i810_audio: AC'97 codec 2 supports AMAP,
total channels = 2


On Tue, 2002-11-12 at 19:43, Alan Cox wrote:

> The kernel knows which AC'97 chip is attached so it could be given a
> table to specify chips where "volume" should either not be presented or
> should be remapped. Do you know what AC97 chip is on your board (Linux
> will print the info in the i810 load, windows and the manual probably
> claim that you have that as your sound chip (typically "Analog
> something" or "Crystal something").
> 



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

* Re: i810 audio
  2002-11-13  0:04       ` Peter Kundrat
@ 2002-11-13  0:43         ` Alan Cox
  2002-11-13 12:08           ` Brian C. Huffman
  0 siblings, 1 reply; 35+ messages in thread
From: Alan Cox @ 2002-11-13  0:43 UTC (permalink / raw)
  To: Peter Kundrat; +Cc: Linux Kernel Mailing List

On Wed, 2002-11-13 at 00:04, Peter Kundrat wrote:
> On Tue, Nov 12, 2002 at 06:43:49PM -0500, Doug Ledford wrote:
> > And in some implementations the codec control labelled PCM2 is actually 
> > main volume, and I've seen one where a headphone was the actual main 
> > volume.  So, the answer is tinker with all the available volume sliders to 
> > see if you can find one that actually changes the volume of everything at 
> > once, and if you do find it, use it.
> 
> Isnt there a way for userspace to somehow find this? It is a bit
> annoying that main volume control in kmix doesnt work (and thus the one
> in panel; also there is no headphone control there).
> The other option would be to configure userspace which control is the
> main one (but windows doesnt need that, so this solution would be
> inferior). Eventually i will take a look what does the win driver (maybe
> it sets main and headphone control always together). Until then i'd like
> to hear what are our options .. since the current situation is not
> really desirable.

The kernel knows which AC'97 chip is attached so it could be given a
table to specify chips where "volume" should either not be presented or
should be remapped. Do you know what AC97 chip is on your board (Linux
will print the info in the i810 load, windows and the manual probably
claim that you have that as your sound chip (typically "Analog
something" or "Crystal something").



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

* Re: i810 audio
  2002-11-12 23:43     ` Doug Ledford
@ 2002-11-13  0:04       ` Peter Kundrat
  2002-11-13  0:43         ` Alan Cox
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Kundrat @ 2002-11-13  0:04 UTC (permalink / raw)
  To: Linux Kernel Mailing List

On Tue, Nov 12, 2002 at 06:43:49PM -0500, Doug Ledford wrote:
> And in some implementations the codec control labelled PCM2 is actually 
> main volume, and I've seen one where a headphone was the actual main 
> volume.  So, the answer is tinker with all the available volume sliders to 
> see if you can find one that actually changes the volume of everything at 
> once, and if you do find it, use it.

Isnt there a way for userspace to somehow find this? It is a bit
annoying that main volume control in kmix doesnt work (and thus the one
in panel; also there is no headphone control there).
The other option would be to configure userspace which control is the
main one (but windows doesnt need that, so this solution would be
inferior). Eventually i will take a look what does the win driver (maybe
it sets main and headphone control always together). Until then i'd like
to hear what are our options .. since the current situation is not
really desirable.

Thanks for any ideas,

pkx
-- 
Peter Kundrat
peter@kundrat.sk

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

* Re: i810 audio
  2002-11-12 23:38   ` Alan Cox
  2002-11-12 23:43     ` Doug Ledford
@ 2002-11-12 23:52     ` Peter Kundrat
  1 sibling, 0 replies; 35+ messages in thread
From: Peter Kundrat @ 2002-11-12 23:52 UTC (permalink / raw)
  To: Linux Kernel Mailing List

On Tue, Nov 12, 2002 at 11:38:04PM +0000, Alan Cox wrote:
> On Tue, 2002-11-12 at 23:06, Brian C. Huffman wrote:
> > Alan / All,
> > 
> > 	I assume this is the patch that's been in your -ac kernels for a 
> > while?  I have an Intel 845GBV board which uses the ICH4 architecture.  
> > I'm happy to report that this patch does allow me to use the integrated 
> > sound on this motherboard, but one thing that I've noticed is that it 
> > seems as though to adjust volume you need to adjust the actual channel 
> > (PCM, CD, etc), rather than main volume.  Any ideas why this might be so?
> 
> It could be the mixer on your board doesnt support main volume control.
> The i8xx audio is half of a whole, it deals with transferring streams of
> audio data and mixer requests down an AC'97 audio bus to a codec which
> does the D/A parts. That codec varies by board.

My board (i815) has also i810 onboard audio and the main control doesnt
work too (i had problmes getting it working with oss driver, but didnt
have much time trying different kernels). 

What works as a main control, though, is control labelled Headphones 
(in alsamixer). 

pkx

-- 
Peter Kundrat
peter@kundrat.sk

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

* Re: i810 audio
  2002-11-12 23:38   ` Alan Cox
@ 2002-11-12 23:43     ` Doug Ledford
  2002-11-13  0:04       ` Peter Kundrat
  2002-11-12 23:52     ` Peter Kundrat
  1 sibling, 1 reply; 35+ messages in thread
From: Doug Ledford @ 2002-11-12 23:43 UTC (permalink / raw)
  To: Alan Cox; +Cc: Brian C. Huffman, Marcelo Tosatti, Linux Kernel Mailing List

On Tue, Nov 12, 2002 at 11:38:04PM +0000, Alan Cox wrote:
> On Tue, 2002-11-12 at 23:06, Brian C. Huffman wrote:
> > Alan / All,
> > 
> > 	I assume this is the patch that's been in your -ac kernels for a 
> > while?  I have an Intel 845GBV board which uses the ICH4 architecture.  
> > I'm happy to report that this patch does allow me to use the integrated 
> > sound on this motherboard, but one thing that I've noticed is that it 
> > seems as though to adjust volume you need to adjust the actual channel 
> > (PCM, CD, etc), rather than main volume.  Any ideas why this might be so?
> 
> It could be the mixer on your board doesnt support main volume control.
> The i8xx audio is half of a whole, it deals with transferring streams of
> audio data and mixer requests down an AC'97 audio bus to a codec which
> does the D/A parts. That codec varies by board.

And in some implementations the codec control labelled PCM2 is actually 
main volume, and I've seen one where a headphone was the actual main 
volume.  So, the answer is tinker with all the available volume sliders to 
see if you can find one that actually changes the volume of everything at 
once, and if you do find it, use it.

On a side note, Alan, this patch looked to be missing the record causes us 
to loose the playback channel patch that you should have.

-- 
  Doug Ledford <dledford@redhat.com>     919-754-3700 x44233
         Red Hat, Inc. 
         1801 Varsity Dr.
         Raleigh, NC 27606
  

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

* Re: i810 audio
  2002-11-12 23:06 ` Brian C. Huffman
@ 2002-11-12 23:38   ` Alan Cox
  2002-11-12 23:43     ` Doug Ledford
  2002-11-12 23:52     ` Peter Kundrat
  0 siblings, 2 replies; 35+ messages in thread
From: Alan Cox @ 2002-11-12 23:38 UTC (permalink / raw)
  To: Brian C. Huffman; +Cc: Marcelo Tosatti, Linux Kernel Mailing List

On Tue, 2002-11-12 at 23:06, Brian C. Huffman wrote:
> Alan / All,
> 
> 	I assume this is the patch that's been in your -ac kernels for a 
> while?  I have an Intel 845GBV board which uses the ICH4 architecture.  
> I'm happy to report that this patch does allow me to use the integrated 
> sound on this motherboard, but one thing that I've noticed is that it 
> seems as though to adjust volume you need to adjust the actual channel 
> (PCM, CD, etc), rather than main volume.  Any ideas why this might be so?

It could be the mixer on your board doesnt support main volume control.
The i8xx audio is half of a whole, it deals with transferring streams of
audio data and mixer requests down an AC'97 audio bus to a codec which
does the D/A parts. That codec varies by board.


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

* Re: i810 audio
  2002-11-10 23:37 i810 audio Alan Cox
@ 2002-11-12 23:06 ` Brian C. Huffman
  2002-11-12 23:38   ` Alan Cox
  0 siblings, 1 reply; 35+ messages in thread
From: Brian C. Huffman @ 2002-11-12 23:06 UTC (permalink / raw)
  To: Alan Cox; +Cc: Marcelo Tosatti, Linux Kernel Mailing List

Alan / All,

	I assume this is the patch that's been in your -ac kernels for a 
while?  I have an Intel 845GBV board which uses the ICH4 architecture.  
I'm happy to report that this patch does allow me to use the integrated 
sound on this motherboard, but one thing that I've noticed is that it 
seems as though to adjust volume you need to adjust the actual channel 
(PCM, CD, etc), rather than main volume.  Any ideas why this might be so?

Brian


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

* i810_audio
@ 2002-11-11 21:16 Rus Foster
  0 siblings, 0 replies; 35+ messages in thread
From: Rus Foster @ 2002-11-11 21:16 UTC (permalink / raw)
  To: linux-kernel

HI,
I've just installed 2.4.20-rc1 to avoid i810 lockups on 2.4.19. However
playing stuff with xmms I've just got

Nov 12 21:03:12 duocity kernel: i810_audio: DMA overrun on write
Nov 12 21:03:12 duocity kernel: i810_audio: CIV 9, LVI 6, hwptr 4808,
count -5640

Also whilst typing this the machine locked hard but nothing in the SYSLOG.
I was moving quickly through my playlist which might of caused an
overrun.  Also is there anyway around the 48Khz only playback as 24Khz
mp3's obviously play to fast
Rgds

Rus

--
http://www.fsck.me.uk - My blog
http://shells.fsck.me.uk - Hosting how you want it.




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

* i810 audio
@ 2002-11-10 23:37 Alan Cox
  2002-11-12 23:06 ` Brian C. Huffman
  0 siblings, 1 reply; 35+ messages in thread
From: Alan Cox @ 2002-11-10 23:37 UTC (permalink / raw)
  To: Marcelo Tosatti, Linux Kernel Mailing List

This is the i810 audio update people keep asking me to submit. Its been
tested in -ac for a long time now

--- ../linux.20rc1/drivers/sound/i810_audio.c	2002-11-10 17:09:55.000000000 +0000
+++ drivers/sound/i810_audio.c	2002-09-19 01:44:31.000000000 +0100
@@ -65,6 +65,17 @@
  *	If you need to force a specific rate set the clocking= option
  *
  *	This driver is cursed. (Ben LaHaise)
+ *
+ *
+ *  ICH 4 caveats
+ *
+ *      The ICH4 has the feature, that the codec ID doesn't have to be 
+ *      congruent with the IO connection.
+ * 
+ *      Therefore, from driver version 0.23 on, there is a "codec ID" <->
+ *      "IO register base offset" mapping (card->ac97_id_map) field.
+ *   
+ *      Juergen "George" Sawinski (jsaw) 
  */
  
 #include <linux/module.h>
@@ -116,6 +127,9 @@
 #ifndef PCI_DEVICE_ID_AMD_768_AUDIO
 #define PCI_DEVICE_ID_AMD_768_AUDIO	0x7445
 #endif
+#ifndef PCI_DEVICE_ID_AMD_8111_AC97
+#define PCI_DEVICE_ID_AMD_8111_AC97	0x746d
+#endif
 
 static int ftsodell=0;
 static int strict_clocking=0;
@@ -126,6 +140,7 @@
 //#define DEBUG2
 //#define DEBUG_INTERRUPTS
 //#define DEBUG_MMAP
+//#define DEBUG_MMIO
 
 #define ADC_RUNNING	1
 #define DAC_RUNNING	2
@@ -168,6 +183,11 @@
  * each dma engine has controlling registers.  These goofy
  * names are from the datasheet, but make it easy to write
  * code while leafing through it.
+ *
+ * ICH4 has 6 dma engines, pcm in, pcm out, mic, pcm in 2, 
+ * mic in 2, s/pdif.   Of special interest is the fact that
+ * the upper 3 DMA engines on the ICH4 *must* be accessed
+ * via mmio access instead of pio access.
  */
 
 #define ENUM_ENGINE(PRE,DIG) 									\
@@ -192,6 +212,14 @@
 	CAS	 = 	0x34			/* Codec Write Semaphore Register */
 };
 
+ENUM_ENGINE(MC2,4);     /* Mic In 2 */
+ENUM_ENGINE(PI2,5);     /* PCM In 2 */
+ENUM_ENGINE(SP,6);      /* S/PDIF */
+
+enum {
+	SDM =           0x80                    /* SDATA_IN Map Register */
+};
+
 /* interrupts for a dma engine */
 #define DMA_INT_FIFO		(1<<4)  /* fifo under/over flow */
 #define DMA_INT_COMPLETE	(1<<3)  /* buffer read/write complete and ioc set */
@@ -211,8 +239,7 @@
 #define INT_GPI		(1<<0)
 #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
 
-
-#define DRIVER_VERSION "0.21"
+#define DRIVER_VERSION "0.24"
 
 /* magic numbers to protect our data structures */
 #define I810_CARD_MAGIC		0x5072696E /* "Prin" */
@@ -221,7 +248,7 @@
 #define NR_HW_CH		3
 
 /* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
-#define NR_AC97		2
+#define NR_AC97                 4
 
 /* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */
 /* stream at a minimum for this card to be happy */
@@ -256,6 +283,25 @@
 	"AMD-8111 IOHub"
 };
 
+/* These are capabilities (and bugs) the chipsets _can_ have */
+static struct {
+	int16_t      nr_ac97;
+#define CAP_MMIO                 0x0001
+#define CAP_20BIT_AUDIO_SUPPORT  0x0002
+	u_int16_t flags;
+} card_cap[] = {
+	{  1, 0x0000 }, /* ICH82801AA */
+	{  1, 0x0000 }, /* ICH82901AB */
+	{  1, 0x0000 }, /* INTEL440MX */
+	{  1, 0x0000 }, /* INTELICH2 */
+	{  2, 0x0000 }, /* INTELICH3 */
+        {  3, 0x0003 }, /* INTELICH4 */
+	/*@FIXME to be verified*/	{  2, 0x0000 }, /* SI7012 */
+	/*@FIXME to be verified*/	{  2, 0x0000 }, /* NVIDIA_NFORCE */
+	/*@FIXME to be verified*/	{  2, 0x0000 }, /* AMD768 */
+	/*@FIXME to be verified*/	{  3, 0x0001 }, /* AMD8111 */
+};
+
 static struct pci_device_id i810_pci_tbl [] __initdata = {
 	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801,
 	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA},
@@ -353,7 +399,6 @@
 

 struct i810_card {
-	struct i810_channel channel[3];
 	unsigned int magic;
 
 	/* We keep i810 cards in a linked list */
@@ -366,6 +411,7 @@
 	/* PCI device stuff */
 	struct pci_dev * pci_dev;
 	u16 pci_id;
+	u16 pci_id_internal; /* used to access card_cap[] */
 #ifdef CONFIG_PM	
 	u16 pm_suspended;
 	u32 pm_save_state[64/sizeof(u32)];
@@ -375,17 +421,27 @@
 	int dev_audio;
 
 	/* structures for abstraction of hardware facilities, codecs, banks and channels*/
+	u16    ac97_id_map[NR_AC97];
 	struct ac97_codec *ac97_codec[NR_AC97];
 	struct i810_state *states[NR_HW_CH];
+	struct i810_channel *channel;	/* 1:1 to states[] but diff. lifetime */
+	dma_addr_t chandma;
 
 	u16 ac97_features;
 	u16 ac97_status;
 	u16 channels;
 	
 	/* hardware resources */
-	unsigned long iobase;
 	unsigned long ac97base;
+	unsigned long iobase;
 	u32 irq;
+
+	unsigned long ac97base_mmio_phys;
+	unsigned long iobase_mmio_phys;
+	u_int8_t *ac97base_mmio;
+	u_int8_t *iobase_mmio;
+
+	int           use_mmio;
 	
 	/* Function support */
 	struct i810_channel *(*alloc_pcm_channel)(struct i810_card *);
@@ -398,6 +454,12 @@
 	int initializing;
 };
 
+/* extract register offset from codec struct */
+#define IO_REG_OFF(codec) (((struct i810_card *) codec->private_data)->ac97_id_map[codec->id])
+
+/* set LVI from CIV */
+#define CIV_TO_LVI(port, off) outb((inb(port+OFF_CIV)+off) & 31, port+OFF_LVI)
+
 static struct i810_card *devs = NULL;
 
 static int i810_open_mixdev(struct inode *inode, struct file *file);
@@ -405,6 +467,10 @@
 			     unsigned int cmd, unsigned long arg);
 static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg);
 static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
+static u16 i810_ac97_get_mmio(struct ac97_codec *dev, u8 reg);
+static void i810_ac97_set_mmio(struct ac97_codec *dev, u8 reg, u16 data);
+static u16 i810_ac97_get_io(struct ac97_codec *dev, u8 reg);
+static void i810_ac97_set_io(struct ac97_codec *dev, u8 reg, u16 data);
 
 static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card)
 {
@@ -756,7 +822,8 @@
 	if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable &&
 	    (dmabuf->trigger & PCM_ENABLE_INPUT)) {
 		dmabuf->enable |= ADC_RUNNING;
-		outb((1<<4) | (1<<2) | 1, state->card->iobase + PI_CR);
+		// Interrupt enable, LVI enable, DMA enable
+		outb(0x10 | 0x04 | 0x01, state->card->iobase + PI_CR);
 	}
 }
 
@@ -805,7 +872,8 @@
 	if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&
 	    (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
 		dmabuf->enable |= DAC_RUNNING;
-		outb((1<<4) | (1<<2) | 1, state->card->iobase + PO_CR);
+		// Interrupt enable, LVI enable, DMA enable
+		outb(0x10 | 0x04 | 0x01, state->card->iobase + PO_CR);
 	}
 }
 static void start_dac(struct i810_state *state)
@@ -957,7 +1025,7 @@
 	  
 		for(i=0;i<dmabuf->numfrag;i++)
 		{
-			sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i);
+			sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i;
 			// the card will always be doing 16bit stereo
 			sg->control=dmabuf->fragsamples;
 			if(state->card->pci_id == PCI_DEVICE_ID_SI_7012)
@@ -972,9 +1040,11 @@
 		}
 		spin_lock_irqsave(&state->card->lock, flags);
 		outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */
-		outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
-		outb(0, state->card->iobase+c->port+OFF_CIV);
-		outb(0, state->card->iobase+c->port+OFF_LVI);
+		while( inb(state->card->iobase+c->port+OFF_CR) & 0x02 ) ;
+		outl((u32)state->card->chandma +
+		    c->num*sizeof(struct i810_channel),
+		    state->card->iobase+c->port+OFF_BDBAR);
+		CIV_TO_LVI(state->card->iobase+c->port, 0);
 
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
@@ -1020,13 +1090,13 @@
 		if(rec && dmabuf->count < dmabuf->dmasize &&
 		   (dmabuf->trigger & PCM_ENABLE_INPUT))
 		{
-			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			CIV_TO_LVI(port, 1);
 			__start_adc(state);
 			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
 		} else if (!rec && dmabuf->count &&
 			   (dmabuf->trigger & PCM_ENABLE_OUTPUT))
 		{
-			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			CIV_TO_LVI(port, 1);
 			__start_dac(state);
 			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
 		}
@@ -1294,7 +1364,6 @@
 				if (dmabuf->enable & ADC_RUNNING)
 					__stop_adc(state);
 				dmabuf->enable = 0;
-				wake_up(&dmabuf->wait);
 #ifdef DEBUG_INTERRUPTS
 				printk(" STOP ");
 #endif
@@ -1740,9 +1809,11 @@
 		}
 		if (c != NULL) {
 			outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */
-			outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
-			outb(0, state->card->iobase+c->port+OFF_CIV);
-			outb(0, state->card->iobase+c->port+OFF_LVI);
+			while ( inb(state->card->iobase+c->port+OFF_CR) & 2 );
+			outl((u32)state->card->chandma +
+			    c->num*sizeof(struct i810_channel),
+			    state->card->iobase+c->port+OFF_BDBAR);
+			CIV_TO_LVI(state->card->iobase+c->port, 0);
 		}
 
 		spin_unlock_irqrestore(&state->card->lock, flags);
@@ -1864,7 +1935,8 @@
 		}
 
 		/* ICH and ICH0 only support 2 channels */
-		if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 ) 
+		if ( state->card->pci_id == PCI_DEVICE_ID_INTEL_82801 
+		     || state->card->pci_id == PCI_DEVICE_ID_INTEL_82901) 
 			return put_user(2, (int *)arg);
 	
 		/* Multi-channel support was added with ICH2. Bits in */
@@ -1883,12 +1955,14 @@
 
 		switch ( val ) {
 			case 2: /* 2 channels is always supported */
-				outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff));
+				outl(i_glob_cnt & 0xffcfffff,
+				     state->card->iobase + GLOB_CNT);
 				/* Do we need to change mixer settings????  */
 				break;
 			case 4: /* Supported on some chipsets, better check first */
 				if ( state->card->channels >= 4 ) {
-					outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000));
+					outl((i_glob_cnt & 0xffcfffff) | 0x100000,
+					      state->card->iobase + GLOB_CNT);
 					/* Do we need to change mixer settings??? */
 				} else {
 					val = ret;
@@ -1896,7 +1970,8 @@
 				break;
 			case 6: /* Supported on some chipsets, better check first */
 				if ( state->card->channels >= 6 ) {
-					outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000));
+					outl((i_glob_cnt & 0xffcfffff) | 0x200000,
+					      state->card->iobase + GLOB_CNT);
 					/* Do we need to change mixer settings??? */
 				} else {
 					val = ret;
@@ -2414,6 +2489,9 @@
 			i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked);
 		} else {
 			i810_set_dac_rate(state, 8000);
+			/* Put the ACLink in 2 channel mode by default */
+			i = inl(card->iobase + GLOB_CNT);
+			outl(i & 0xffcfffff, card->iobase + GLOB_CNT);
 		}
 	}
 		
@@ -2478,27 +2556,86 @@
 
 /* Write AC97 codec registers */
 
-static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg)
+static u16 i810_ac97_get_mmio(struct ac97_codec *dev, u8 reg)
 {
 	struct i810_card *card = dev->private_data;
 	int count = 100;
-	u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));
+	u16 reg_set = IO_REG_OFF(dev) | (reg&0x7f);
+	
+	while(count-- && (readb(card->iobase_mmio + CAS) & 1)) 
+		udelay(1);
+	
+#ifdef DEBUG_MMIO
+	{
+		u16 ans = readw(card->ac97base_mmio + reg_set);
+		printk(KERN_DEBUG "i810_audio: ac97_get_mmio(%d) -> 0x%04X\n", ((int) reg_set) & 0xffff, (u32) ans);
+		return ans;
+	}
+#else
+	return readw(card->ac97base_mmio + reg_set);
+#endif
+}
 
+static u16 i810_ac97_get_io(struct ac97_codec *dev, u8 reg)
+{
+	struct i810_card *card = dev->private_data;
+	int count = 100;
+	u16 reg_set = IO_REG_OFF(dev) | (reg&0x7f);
+	
 	while(count-- && (inb(card->iobase + CAS) & 1)) 
 		udelay(1);
 	
 	return inw(card->ac97base + reg_set);
 }
 
-static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data)
+static void i810_ac97_set_mmio(struct ac97_codec *dev, u8 reg, u16 data)
 {
 	struct i810_card *card = dev->private_data;
 	int count = 100;
-	u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f));
+	u16 reg_set = IO_REG_OFF(dev) | (reg&0x7f);
+	
+	while(count-- && (readb(card->iobase_mmio + CAS) & 1)) 
+		udelay(1);
+	
+	writew(data, card->ac97base_mmio + reg_set);
 
+#ifdef DEBUG_MMIO
+	printk(KERN_DEBUG "i810_audio: ac97_set_mmio(0x%04X, %d)\n", (u32) data, ((int) reg_set) & 0xffff);
+#endif
+}
+
+static void i810_ac97_set_io(struct ac97_codec *dev, u8 reg, u16 data)
+{
+	struct i810_card *card = dev->private_data;
+	int count = 100;
+	u16 reg_set = IO_REG_OFF(dev) | (reg&0x7f);
+	
 	while(count-- && (inb(card->iobase + CAS) & 1)) 
 		udelay(1);
-	outw(data, card->ac97base + reg_set);
+	
+        outw(data, card->ac97base + reg_set);
+}
+
+static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg)
+{
+	struct i810_card *card = dev->private_data;
+	if (card->use_mmio) {
+		return i810_ac97_get_mmio(dev, reg);
+	}
+	else {
+		return i810_ac97_get_io(dev, reg);
+	}
+}
+
+static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data)
+{
+	struct i810_card *card = dev->private_data;
+	if (card->use_mmio) {
+		i810_ac97_set_mmio(dev, reg, data);
+	}
+	else {
+		i810_ac97_set_io(dev, reg, data);
+	}
 }
 

@@ -2523,7 +2660,7 @@
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/20);
 		}
-		for (i = 0; i < NR_AC97 && card && !card->initializing; i++)
+		for (i = 0; i < NR_AC97 && card && !card->initializing; i++) 
 			if (card->ac97_codec[i] != NULL &&
 			    card->ac97_codec[i]->dev_mixer == minor) {
 				file->private_data = card->ac97_codec[i];
@@ -2551,10 +2688,18 @@
 /* AC97 codec initialisation.  These small functions exist so we don't
    duplicate code between module init and apm resume */
 
-static inline int i810_ac97_exists(struct i810_card *card,int ac97_number)
+static inline int i810_ac97_exists(struct i810_card *card, int ac97_number)
 {
 	u32 reg = inl(card->iobase + GLOB_STA);
-	return (reg & (0x100 << ac97_number));
+	switch (ac97_number) {
+	case 0:
+		return reg & (1<<8);
+	case 1: 
+		return reg & (1<<9);
+	case 2:
+		return reg & (1<<28);
+	}
+	return 0;
 }
 
 static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec)
@@ -2577,10 +2722,9 @@
 	/* power it all up */
 	i810_ac97_set(codec, AC97_POWER_CONTROL,
 		      i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
+
 	/* wait for analog ready */
-	for (i=10;
-	     i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf);
-	     i--)
+	for (i=10; i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--)
 	{
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(HZ/20);
@@ -2588,11 +2732,18 @@
 	return i;
 }
 
-/* if I knew what this did, I'd give it a better name */
-static int i810_ac97_random_init_stuff(struct i810_card *card)
+/**
+ *	i810_ac97_power_up_bus	-	bring up AC97 link
+ *	@card : ICH audio device to power up
+ *
+ *	Bring up the ACLink AC97 codec bus
+ */
+ 
+static int i810_ac97_power_up_bus(struct i810_card *card)
 {	
 	u32 reg = inl(card->iobase + GLOB_CNT);
 	int i;
+	int primary_codec_id = 0;
 
 	if((reg&2)==0)	/* Cold required */
 		reg|=2;
@@ -2600,8 +2751,13 @@
 		reg|=4;	/* Warm */
 		
 	reg&=~8;	/* ACLink on */
-	outl(reg , card->iobase + GLOB_CNT);
 	
+	/* At this point we deassert AC_RESET # */
+	outl(reg , card->iobase + GLOB_CNT);
+
+	/* We must now allow time for the Codec initialisation.
+	   600mS is the specified time */
+	   	
 	for(i=0;i<10;i++)
 	{
 		if((inl(card->iobase+GLOB_CNT)&4)==0)
@@ -2618,7 +2774,31 @@
 
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout(HZ/2);
-	reg = inl(card->iobase + GLOB_STA);
+
+	/*
+	 *	See if the primary codec comes ready. This must happen
+	 *	before we start doing DMA stuff
+	 */	
+	/* see i810_ac97_init for the next 7 lines (jsaw) */
+	inw(card->ac97base);
+	if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4)
+	    && (card->use_mmio)) {
+		primary_codec_id = (int) readl(card->iobase_mmio + SDM) & 0x3;
+		printk(KERN_INFO "i810_audio: Primary codec has ID %d\n",
+		       primary_codec_id);
+	}
+
+	if(! i810_ac97_exists(card, primary_codec_id))
+	{
+		printk(KERN_INFO "i810_audio: Codec not ready.. wait.. ");
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ);	/* actually 600mS by the spec */
+
+		if(i810_ac97_exists(card, primary_codec_id))
+			printk("OK\n");
+		else 
+			printk("no response.\n");
+	}
 	inw(card->ac97base);
 	return 1;
 }
@@ -2626,12 +2806,14 @@
 static int __init i810_ac97_init(struct i810_card *card)
 {
 	int num_ac97 = 0;
+	int ac97_id;
 	int total_channels = 0;
+	int nr_ac97_max = card_cap[card->pci_id_internal].nr_ac97;
 	struct ac97_codec *codec;
 	u16 eid;
 	u32 reg;
 
-	if(!i810_ac97_random_init_stuff(card)) return 0;
+	if(!i810_ac97_power_up_bus(card)) return 0;
 
 	/* Number of channels supported */
 	/* What about the codec?  Just because the ICH supports */
@@ -2647,26 +2829,47 @@
 		card->channels = 6;
 	else if ( reg & 0x0100000 )
 		card->channels = 4;
-	printk("i810_audio: Audio Controller supports %d channels.\n", card->channels);
+	printk(KERN_INFO "i810_audio: Audio Controller supports %d channels.\n", card->channels);
+	printk(KERN_INFO "i810_audio: Defaulting to base 2 channel mode.\n");
+	reg = inl(card->iobase + GLOB_CNT);
+	outl(reg & 0xffcfffff, card->iobase + GLOB_CNT);
 		
-	inw(card->ac97base);
+	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) 
+		card->ac97_codec[num_ac97] = NULL;
 
-	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+	/*@FIXME I don't know, if I'm playing to safe here... (jsaw) */
+	if ((nr_ac97_max > 2) && !card->use_mmio) nr_ac97_max = 2;
 
-		/* Assume codec isn't available until we go through the
-		 * gauntlet below */
-		card->ac97_codec[num_ac97] = NULL;
+	for (num_ac97 = 0; num_ac97 < nr_ac97_max; num_ac97++) {
+		/* codec reset */
+		printk(KERN_INFO "i810_audio: Resetting connection %d\n", num_ac97);
+		if (card->use_mmio) readw(card->ac97base_mmio + 0x80*num_ac97);
+		else inw(card->ac97base + 0x80*num_ac97);
+
+		/* If we have the SDATA_IN Map Register, as on ICH4, we
+		   do not loop thru all possible codec IDs but thru all 
+		   possible IO channels. Bit 0:1 of SDM then holds the 
+		   last codec ID spoken to. 
+		*/
+		if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4)
+		    && (card->use_mmio)) {
+			ac97_id = (int) readl(card->iobase_mmio + SDM) & 0x3;
+			printk(KERN_INFO "i810_audio: Connection %d with codec id %d\n",
+			       num_ac97, ac97_id);
+		}
+		else {
+			ac97_id = num_ac97;
+		}
 
 		/* The ICH programmer's reference says you should   */
 		/* check the ready status before probing. So we chk */
 		/*   What do we do if it's not ready?  Wait and try */
 		/*   again, or abort?                               */
-		if (!i810_ac97_exists(card,num_ac97)) {
+		if (!i810_ac97_exists(card, ac97_id)) {
 			if(num_ac97 == 0)
 				printk(KERN_ERR "i810_audio: Primary codec not ready.\n");
-			break; /* I think this works, if not ready stop */
 		}
-
+		
 		if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
 			return -ENOMEM;
 		memset(codec, 0, sizeof(struct ac97_codec));
@@ -2674,13 +2877,20 @@
 		/* initialize some basic codec information, other fields will be filled
 		   in ac97_probe_codec */
 		codec->private_data = card;
-		codec->id = num_ac97;
+		codec->id = ac97_id;
+		card->ac97_id_map[ac97_id] = num_ac97 * 0x80;
 
-		codec->codec_read = i810_ac97_get;
-		codec->codec_write = i810_ac97_set;
+		if (card->use_mmio) {	
+			codec->codec_read = i810_ac97_get_mmio;
+			codec->codec_write = i810_ac97_set_mmio;
+		}
+		else {
+			codec->codec_read = i810_ac97_get_io;
+			codec->codec_write = i810_ac97_set_io;
+		}
 	
 		if(!i810_ac97_probe_and_powerup(card,codec)) {
-			printk("i810_audio: timed out waiting for codec %d analog ready.\n", num_ac97);
+			printk(KERN_ERR "i810_audio: timed out waiting for codec %d analog ready.\n", ac97_id);
 			kfree(codec);
 			break;	/* it didn't work */
 		}
@@ -2689,7 +2899,7 @@
 		
 		/* Don't attempt to get eid until powerup is complete */
 		eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
-		
+
 		if(eid==0xFFFFFF)
 		{
 			printk(KERN_WARNING "i810_audio: no codec attached ?\n");
@@ -2697,16 +2907,27 @@
 			break;
 		}
 		
+		/* Check for an AC97 1.0 soft modem (ID1) */
+		
+		if(codec->codec_read(codec, AC97_RESET) & 2)
+		{
+			printk(KERN_WARNING "i810_audio: codec %d is an AC97 1.0 softmodem - skipping.\n", ac97_id);
+			kfree(codec);
+			continue;
+		}
+		
+		/* Check for an AC97 2.x soft modem */
+		
 		codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
-		if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID))
+		if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
 		{
-			printk(KERN_WARNING "i810_audio: codec %d is a softmodem - skipping.\n", num_ac97);
+			printk(KERN_WARNING "i810_audio: codec %d is an AC97 2.x softmodem - skipping.\n", ac97_id);
 			kfree(codec);
 			continue;
 		}
 	
 		card->ac97_features = eid;
-				
+
 		/* Now check the codec for useful features to make up for
 		   the dumbness of the 810 hardware engine */
 
@@ -2720,6 +2941,11 @@
 			}			
 		}
    		
+		/* Turn on the amplifier */
+
+		codec->codec_write(codec, AC97_POWER_CONTROL, 
+			 codec->codec_read(codec, AC97_POWER_CONTROL) & ~0x8000);
+				
 		/* Determine how many channels the codec(s) support   */
 		/*   - The primary codec always supports 2            */
 		/*   - If the codec supports AMAP, surround DACs will */
@@ -2745,7 +2971,7 @@
 				total_channels += 2;
 			if (eid & 0x0140) /* LFE and Center channels */
 				total_channels += 2;
-			printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels);
+			printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", ac97_id, total_channels);
 		} else if (eid & 0x0400) {  /* this only works on 2.2 compliant codecs */
 			eid &= 0xffcf;
 			if((eid & 0xc000) != 0)	{
@@ -2767,14 +2993,14 @@
 			}
 			i810_ac97_set(codec, AC97_EXTENDED_ID, eid);
 			eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
-			printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid);
+			printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", ac97_id, eid);
 			if (eid & 0x0080) /* L/R Surround channels */
 				total_channels += 2;
 			if (eid & 0x0140) /* LFE and Center channels */
 				total_channels += 2;
-			printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels);
+			printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", ac97_id, total_channels);
 		} else {
-			printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels);
+			printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", ac97_id, total_channels);
 		}
 
 		if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
@@ -2823,6 +3049,8 @@
 		init_MUTEX(&state->open_sem);
 		dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
 		dmabuf->trigger = PCM_ENABLE_OUTPUT;
+		i810_set_spdif_output(state, -1, 0);
+		i810_set_dac_channels(state, 2);
 		i810_set_dac_rate(state, 48000);
 		if(prog_dmabuf(state, 0) != 0) {
 			goto config_out_nodmabuf;
@@ -2831,7 +3059,7 @@
 			goto config_out;
 		}
 		dmabuf->count = dmabuf->dmasize;
-		outb(31,card->iobase+dmabuf->write_channel->port+OFF_LVI);
+		CIV_TO_LVI(card->iobase+dmabuf->write_channel->port, 31);
 		save_flags(flags);
 		cli();
 		start_dac(state);
@@ -2839,10 +3067,9 @@
 		mdelay(50);
 		new_offset = i810_get_dma_addr(state, 0);
 		stop_dac(state);
-		outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR);
 		restore_flags(flags);
 		i = new_offset - offset;
-#ifdef DEBUG
+#ifdef DEBUG_INTERRUPTS
 		printk("i810_audio: %d bytes in 50 milliseconds\n", i);
 #endif
 		if(i == 0)
@@ -2884,10 +3111,25 @@
 	memset(card, 0, sizeof(*card));
 
 	card->initializing = 1;
-	card->iobase = pci_resource_start (pci_dev, 1);
-	card->ac97base = pci_resource_start (pci_dev, 0);
 	card->pci_dev = pci_dev;
 	card->pci_id = pci_id->device;
+	card->ac97base = pci_resource_start (pci_dev, 0);
+	card->iobase = pci_resource_start (pci_dev, 1);
+
+	/* if chipset could have mmio capability, check it */ 
+	if (card_cap[pci_id->driver_data].flags & CAP_MMIO) {
+		card->ac97base_mmio_phys = pci_resource_start (pci_dev, 2);
+		card->iobase_mmio_phys = pci_resource_start (pci_dev, 3);
+
+		if ((card->ac97base_mmio_phys) && (card->iobase_mmio_phys)) {
+			card->use_mmio = 1;
+		}
+		else {
+			card->ac97base_mmio_phys = 0;
+			card->iobase_mmio_phys = 0;
+		}
+	}
+
 	card->irq = pci_dev->irq;
 	card->next = devs;
 	card->magic = I810_CARD_MAGIC;
@@ -2899,23 +3141,37 @@
 
 	pci_set_master(pci_dev);
 
-	printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n",
-	       card_names[pci_id->driver_data], card->iobase, card->ac97base, 
+	printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, "
+	       "MEM 0x%04lx and 0x%04lx, IRQ %d\n",
+	       card_names[pci_id->driver_data], 
+	       card->iobase, card->ac97base, 
+	       card->ac97base_mmio_phys, card->iobase_mmio_phys,
 	       card->irq);
 
 	card->alloc_pcm_channel = i810_alloc_pcm_channel;
 	card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel;
 	card->alloc_rec_mic_channel = i810_alloc_rec_mic_channel;
 	card->free_pcm_channel = i810_free_pcm_channel;
-	card->channel[0].offset = 0;
-	card->channel[0].port = 0x00;
-	card->channel[0].num=0;
-	card->channel[1].offset = 0;
-	card->channel[1].port = 0x10;
-	card->channel[1].num=1;
-	card->channel[2].offset = 0;
-	card->channel[2].port = 0x20;
-	card->channel[2].num=2;
+
+	if ((card->channel = pci_alloc_consistent(pci_dev,
+	    sizeof(struct i810_channel)*NR_HW_CH, &card->chandma)) == NULL) {
+		printk(KERN_ERR "i810: cannot allocate channel DMA memory\n");
+		goto out_mem;
+	}
+
+	{ /* We may dispose of this altogether some time soon, so... */
+		struct i810_channel *cp = card->channel;
+
+		cp[0].offset = 0;
+		cp[0].port = 0x00;
+		cp[0].num = 0;
+		cp[1].offset = 0;
+		cp[1].port = 0x10;
+		cp[1].num = 1;
+		cp[2].offset = 0;
+		cp[2].port = 0x20;
+		cp[2].num = 2;
+	}
 
 	/* claim our iospace and irq */
 	request_region(card->iobase, 64, card_names[pci_id->driver_data]);
@@ -2924,19 +3180,42 @@
 	if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ,
 			card_names[pci_id->driver_data], card)) {
 		printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq);
-		release_region(card->iobase, 64);
-		release_region(card->ac97base, 256);
-		kfree(card);
-		return -ENODEV;
+		goto out_pio;
+	}
+
+	if (card->use_mmio) {
+		if (request_mem_region(card->ac97base_mmio_phys, 512, "ich_audio MMBAR")) {
+			if ((card->ac97base_mmio = ioremap(card->ac97base_mmio_phys, 512))) { /*@FIXME can ioremap fail? don't know (jsaw) */
+				if (request_mem_region(card->iobase_mmio_phys, 256, "ich_audio MBBAR")) {
+					if ((card->iobase_mmio = ioremap(card->iobase_mmio_phys, 256))) {
+						printk(KERN_INFO "i810: %s mmio at 0x%04lx and 0x%04lx\n",
+						       card_names[pci_id->driver_data], 
+						       (unsigned long) card->ac97base_mmio, 
+						       (unsigned long) card->iobase_mmio); 
+					}
+					else {
+						iounmap(card->ac97base_mmio);
+						release_mem_region(card->ac97base_mmio_phys, 512);
+						release_mem_region(card->iobase_mmio_phys, 512);
+						card->use_mmio = 0;
+					}
+				}
+				else {
+					iounmap(card->ac97base_mmio);
+					release_mem_region(card->ac97base_mmio_phys, 512);
+					card->use_mmio = 0;
+				}
+			}
+		}
+		else {
+			card->use_mmio = 0;
+		}
 	}
 
 	/* initialize AC97 codec and register /dev/mixer */
 	if (i810_ac97_init(card) <= 0) {
-		release_region(card->iobase, 64);
-		release_region(card->ac97base, 256);
 		free_irq(card->irq, card);
-		kfree(card);
-		return -ENODEV;
+		goto out_iospace;
 	}
 	pci_set_drvdata(pci_dev, card);
 
@@ -2949,19 +3228,34 @@
 	if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) {
 		int i;
 		printk(KERN_ERR "i810_audio: couldn't register DSP device!\n");
-		release_region(card->iobase, 64);
-		release_region(card->ac97base, 256);
 		free_irq(card->irq, card);
 		for (i = 0; i < NR_AC97; i++)
 		if (card->ac97_codec[i] != NULL) {
 			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
 			kfree (card->ac97_codec[i]);
 		}
-		kfree(card);
-		return -ENODEV;
+		goto out_iospace;
 	}
+
  	card->initializing = 0;
 	return 0;
+
+out_iospace:
+	if (card->use_mmio) {
+		iounmap(card->ac97base_mmio);
+		iounmap(card->iobase_mmio);
+		release_mem_region(card->ac97base_mmio_phys, 512);
+		release_mem_region(card->iobase_mmio_phys, 256);
+	}
+out_pio:	
+	release_region(card->iobase, 64);
+	release_region(card->ac97base, 256);
+out_chan:
+	pci_free_consistent(pci_dev, sizeof(struct i810_channel)*NR_HW_CH,
+	    card->channel, card->chandma);
+out_mem:
+	kfree(card);
+	return -ENODEV;
 }
 
 static void __devexit i810_remove(struct pci_dev *pci_dev)
@@ -2972,6 +3266,12 @@
 	free_irq(card->irq, devs);
 	release_region(card->iobase, 64);
 	release_region(card->ac97base, 256);
+	if (card->use_mmio) {
+		iounmap(card->ac97base_mmio);
+		iounmap(card->iobase_mmio);
+		release_mem_region(card->ac97base_mmio_phys, 512);
+		release_mem_region(card->iobase_mmio_phys, 256);
+	}
 
 	/* unregister audio devices */
 	for (i = 0; i < NR_AC97; i++)
@@ -3054,7 +3354,7 @@
 	   hardware has to be more or less completely reinitialized from
 	   scratch after an apm suspend.  Works For Me.   -dan */
 
-	i810_ac97_random_init_stuff(card);
+	i810_ac97_power_up_bus(card);
 
 	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
 		struct ac97_codec *codec = card->ac97_codec[num_ac97];

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

* Re: i810_audio
  2002-01-10 19:21 i810_audio reddog83
@ 2002-01-11  0:33 ` Doug Ledford
  0 siblings, 0 replies; 35+ messages in thread
From: Doug Ledford @ 2002-01-11  0:33 UTC (permalink / raw)
  To: reddog83; +Cc: camel_3, linux-kernel

reddog83 wrote:

> Doug do you still have the i810 audio version 0.18
> Also v0.19 has unresolved symbols :
> ./i810_audio.o: unresolved symbol __global_cli
> ./i810_audio.o: unresolved symbol kernel_flag
> ./i810_audio.o: unresolved symbol synchronize_irq
> ./i810_audio.o: unresolved symbol __global_save_flags
> ./i810_audio.o: unresolved symbol __global_restore_flags


This isn't an i810_audio.c problem.  You've somehow changed your config from 
smp to non-smp or vice versa and not recompiled the rest of the kernel yet 
or something like that.  Besides, 0.20 is on my site now and it should solve 
the last of the lockups some people were seeing with artsd.


> i686 566 Celeron kernel 2.4.17 
> If you do will you send me v0.18 so that I may have sound again thank you.
> (I lost it so that is why I'm asking)
> Victor
> 
>>Does anybody still have the i810_audio version 0.18 if so please send me the 
>>driver becuase Doug updated his link for version 0.19
>>Thank you in advance Victor
>>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 
> 



-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
@ 2002-01-10 19:21 reddog83
  2002-01-11  0:33 ` i810_audio Doug Ledford
  0 siblings, 1 reply; 35+ messages in thread
From: reddog83 @ 2002-01-10 19:21 UTC (permalink / raw)
  To: camel_3; +Cc: linux-kernel

Doug do you still have the i810 audio version 0.18
Also v0.19 has unresolved symbols :
./i810_audio.o: unresolved symbol __global_cli
./i810_audio.o: unresolved symbol kernel_flag
./i810_audio.o: unresolved symbol synchronize_irq
./i810_audio.o: unresolved symbol __global_save_flags
./i810_audio.o: unresolved symbol __global_restore_flags
i686 566 Celeron kernel 2.4.17 
If you do will you send me v0.18 so that I may have sound again thank you.
(I lost it so that is why I'm asking)
Victor
>Does anybody still have the i810_audio version 0.18 if so please send me the 
>driver becuase Doug updated his link for version 0.19
>Thank you in advance Victor

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

* Re: i810_audio
@ 2002-01-10 18:59 reddog83
  0 siblings, 0 replies; 35+ messages in thread
From: reddog83 @ 2002-01-10 18:59 UTC (permalink / raw)
  To: reddog83; +Cc: linux-kernel

Does anybody still have the i810_audio version 0.18 if so please send me the
driver becuase Doug updated his link for version 0.19
Thank you in advance Victor

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

* Re: i810_audio
@ 2002-01-10  6:42 reddog83
  0 siblings, 0 replies; 35+ messages in thread
From: reddog83 @ 2002-01-10  6:42 UTC (permalink / raw)
  To: reddog83; +Cc: linux-kernel

Does anybody still have the i810_audio version 0.18 if so please send me the 
driver becuase Doug updated his link for version 0.19
Thank you in advance Victor

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

* Re: i810_audio
  2002-01-08  9:02           ` i810_audio Doug Ledford
  2002-01-08 15:11             ` i810_audio Mario Mikocevic
@ 2002-01-09 15:47             ` Mario Mikocevic
  1 sibling, 0 replies; 35+ messages in thread
From: Mario Mikocevic @ 2002-01-09 15:47 UTC (permalink / raw)
  To: Doug Ledford; +Cc: linux-kernel

Hi,

On Tue, Jan 08, 2002 at 04:02:35AM -0500, Doug Ledford wrote:
> OK, various clean ups made, and enough of the SiS code included that I think 
> it should work, plus one change to the i810 interrupt handler that will 
> (hopefully) render the other change you made to get_dma_addr and drain_dac 
> unnecessary.  If people could please download and test the new 0.14 version 
> of the driver on my site, I would appreciate it.
> 
> http://people.redhat.com/dledford/i810_audio.c.gz

Well, ver 0.18 works fine for me _and_ for a friend of mine who _had_
problems with high network traffic while playing sound.

I haven't tried it on my webcast farm yet.


Intel 810 + AC97 Audio, version 0.18, 09:27:34 Jan  9 2002
PCI: Found IRQ 9 for device 00:1f.5
PCI: Sharing IRQ 9 with 00:1f.3
PCI: Sharing IRQ 9 with 02:09.0
PCI: Setting latency timer of device 00:1f.5 to 64
i810: Intel ICH2 found at IO 0xc400 and 0xc300, IRQ 9
i810_audio: Audio Controller supports 6 channels.
ac97_codec: AC97 Audio codec, id: 0x4352:0x5934 (Cirrus Logic CS4299 rev D)
i810_audio: AC'97 codec 0 supports AMAP, total channels = 2

-- 
Mario Mikočević (Mozgy)
mozgy at hinet dot hr
My favourite FUBAR ...

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

* Re: i810_audio
  2002-01-08 19:58 ` i810_audio Doug Ledford
  2002-01-08 23:03   ` i810_audio willy tarreau
  2002-01-09  6:28   ` i810_audio Nick Papadonis
@ 2002-01-09  7:16   ` willy tarreau
  2 siblings, 0 replies; 35+ messages in thread
From: willy tarreau @ 2002-01-09  7:16 UTC (permalink / raw)
  To: Doug Ledford; +Cc: Mario Mikocevic, linux-kernel

> Please give it a try and let me know how it works.

Hello Doug,

same effect here : version 0.16 still hangs my system
after
close. I'll try to take some time to find which part
of thomas'
driver make it work here.

Regards,
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Courrier : http://courrier.yahoo.fr

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

* Re: i810_audio
@ 2002-01-09  7:09 reddog83
  0 siblings, 0 replies; 35+ messages in thread
From: reddog83 @ 2002-01-09  7:09 UTC (permalink / raw)
  To: reddog83; +Cc: linux-kernel

The i810 audio on the I810 chipset is working with Doug's 0.18 release.
Kde love's man. I haven't had sound for a while since I updated Kde-2.1.1 to 
Kde-2.2.
Thank you
Victor

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

* Re: i810_audio
  2002-01-08 19:58 ` i810_audio Doug Ledford
  2002-01-08 23:03   ` i810_audio willy tarreau
@ 2002-01-09  6:28   ` Nick Papadonis
  2002-01-09  7:16   ` i810_audio willy tarreau
  2 siblings, 0 replies; 35+ messages in thread
From: Nick Papadonis @ 2002-01-09  6:28 UTC (permalink / raw)
  To: Doug Ledford; +Cc: willy tarreau, Mario Mikocevic, linux-kernel

Doug Ledford <dledford@redhat.com> writes:
> Someone posted one of the DMA Overrun on write error messages to me,
> and that allowed me to see that on the SiS hardware we are getting
> garbage in the upper 3 bits of the LVI register (presumably because we
> read garbage from the upper 3 bits of the CIV register).  So, I've put
> a 0.15 version of my driver on my site that now bounds our LVI and CIV
> reads so that we mask out any possible garbage.  And, since writing
> garbage to LVI could keep the hardware going in loops forever and
> other sorts of bad things, it might solve your problem.  Please give
> it a try and let me know how it works.
> 

0.15 works better then 0.13 for me.  I haven't had any problems yet.

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

* Re: i810_audio
  2002-01-08 19:58 ` i810_audio Doug Ledford
@ 2002-01-08 23:03   ` willy tarreau
  2002-01-09  6:28   ` i810_audio Nick Papadonis
  2002-01-09  7:16   ` i810_audio willy tarreau
  2 siblings, 0 replies; 35+ messages in thread
From: willy tarreau @ 2002-01-08 23:03 UTC (permalink / raw)
  To: Doug Ledford; +Cc: Mario Mikocevic, linux-kernel

> that allowed me to see that on the SiS hardware we

I'm on intel only, no SiS here.

> solve your problem.  Please give it a try and let me
> know how it works.

I'll give it a try tomorrow morning (this little
beast it at work).

Regards,
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Courrier : http://courrier.yahoo.fr

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

* Re: i810_audio
  2002-01-08 20:01         ` i810_audio Nathan Bryant
  2002-01-08 20:15           ` i810_audio Doug Ledford
@ 2002-01-08 20:23           ` Nathan Bryant
  1 sibling, 0 replies; 35+ messages in thread
From: Nathan Bryant @ 2002-01-08 20:23 UTC (permalink / raw)
  To: Nathan Bryant; +Cc: Doug Ledford, Thomas Gschwind, linux-kernel

Nathan Bryant wrote:

> 1) Is the LVI interrupt supposed to arrive when the chip *starts* 
> playing the last buffer?
> 2) Does SiS actually do it this way?
>
> If your theory on why the registers are spinning is correct, and if we 
> receive the LVI interrupt with too much latency, your code will still 
> deadlock, Doug. (The LVI interrupt handler calls update_ptr first 
> thing, which calls get_dma_address.) Furthermore, if this turns out to 
> be the case, the LVI IRQ handler uses dmabuf->count to determine 
> whether to call stop_dac, and needs to call update_ptr to update 
> dmabuf->count... so an explicit stop_dac might be needed elsewhere.
>
> Even if the LVI interrupt comes at the beginning of the buffer, those 
> 2048 bytes will play in 10.67 ms. Can we really guarantee that kind of 
> latency?


Add to this, if SiS isn't sending DCH, and LVI arrives at the beginning 
of the last buffer, count is still > 0, so we don't call stop_dac. And 
we're right back where we started.


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

* Re: i810_audio
  2002-01-08 20:01         ` i810_audio Nathan Bryant
@ 2002-01-08 20:15           ` Doug Ledford
  2002-01-08 20:23           ` i810_audio Nathan Bryant
  1 sibling, 0 replies; 35+ messages in thread
From: Doug Ledford @ 2002-01-08 20:15 UTC (permalink / raw)
  To: Nathan Bryant; +Cc: Thomas Gschwind, linux-kernel

Nathan Bryant wrote:

> Doug Ledford wrote:
> 
>>>  Added stop_dac at the end.  Otherwise the system gets locked up 
>>> sometimes.
>>
>>
>>
>>
>> This is (I think) a red herring.  I would suspect that the lockup you 
>> are seeing is more likely to have something to do with the changes in 
>> i810_get_dma_address().  I'm referring to the change to the do 
>> {}while() loop in particular.  Also, if the change you made in 
>> i810_get_dma_address() is needed for the SiS chipset, then there would 
>> appear to be a hardware bug that needs worked around.  However, I 
>> think this is the wrong way to solve it.  In prog_dmabuf(), we set the 
>> control bits on the device such that when it finishes the LVI, it's 
>> *suppossed* to stop playback.  Now, the only way I can think of that 
>> the CIV register would update as fast as you can read it in 
>> i810_get_dma_address() is if at the end of LVI, the card went into a 
>> loop and the CIV is essentially running from 0 to 31 and going round 
>> and round without doing anything.  In that case, the proper fix may 
>> actually be to go into the interrupt handler and on LVI interrupt with 
>> count==0 (for playback) call stop_dac and do similarly for record.  
>> This is because I suspect that we aren't ever getting the DCH Stopped 
>> interrupt on the SiS card.  Of course, the DCH Stop interrupt causes 
>> us to call stop_{dac,adc} as appropriate, so the call to stop_dac in 
>> drain_dac is redundant if this code is working as advertised.  I 
>> suspect that fixing things in the interrupt handler will make the 
>> stop_dac call in drain_dac and the change in i810_get_dma_address() 
>> both unnecessary.  Can you test that for me? 
> 
> 
> 1) Is the LVI interrupt supposed to arrive when the chip *starts* 
> playing the last buffer?


No, when the last buffer is complete.


> 2) Does SiS actually do it this way?


No clue.  If they follow the spec, then yes.


> If your theory on why the registers are spinning is correct, and if we 
> receive the LVI interrupt with too much latency, your code will still 
> deadlock, Doug. (The LVI interrupt handler calls update_ptr first thing, 
> which calls get_dma_address.)


My theory about get_dma_address at this point is that the upper 3 bits of 
CIV are garbage and are subject to different states each time you read the 
register.  Since I bounded the read by using &31 to mask off the garbage 
bits, that should no longer be an issue.

> Furthermore, if this turns out to be the 
> case, the LVI IRQ handler uses dmabuf->count to determine whether to 
> call stop_dac, and needs to call update_ptr to update dmabuf->count... 
> so an explicit stop_dac might be needed elsewhere.


I don't think so.  See above.


> Even if the LVI interrupt comes at the beginning of the buffer, those 
> 2048 bytes will play in 10.67 ms. Can we really guarantee that kind of 
> latency?
> 

We don't need to.  This is also where the spurious DMA Overrun on write 
errors were coming from.  We *expect* to have a dma overrun on write if the 
last block was not completely filled (notice in i810_write() that we filled 
the remainder of the block with silence).  So, we check if LVI == CIV when 
we appear to have an overrun.  If they are the same, then no overrun, we 
just played the silence until the end of the buffer, then things stopped 
because this was the LVI, and all is good and right in the world.  If they 
aren't the same (which they aren't if you are getting garbage in the upper 3 
bits that is random), then we print out spurious messages.  I think a lot of 
the problems that people have been chasing with the SiS chipset are this 
exact kind of thing.  You'll also notice that support for the NVIDIA nForce 
Audio is now in 0.15.  Based upon the fact that the guy from NVIDIA that 
contacted me had been seeing the same sort of random DMA Overrun on write 
messages, I'm guessing that both the SiS and the NVIDIA audio devices have 
this same garbage in the upper 3 bits of CIV register problem.




-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
  2002-01-08  7:59       ` i810_audio Doug Ledford
  2002-01-08  8:11         ` i810_audio Doug Ledford
@ 2002-01-08 20:01         ` Nathan Bryant
  2002-01-08 20:15           ` i810_audio Doug Ledford
  2002-01-08 20:23           ` i810_audio Nathan Bryant
  1 sibling, 2 replies; 35+ messages in thread
From: Nathan Bryant @ 2002-01-08 20:01 UTC (permalink / raw)
  To: Doug Ledford; +Cc: Thomas Gschwind, linux-kernel

Doug Ledford wrote:

>>  Added stop_dac at the end.  Otherwise the system gets locked up 
>> sometimes.
>
>
>
> This is (I think) a red herring.  I would suspect that the lockup you 
> are seeing is more likely to have something to do with the changes in 
> i810_get_dma_address().  I'm referring to the change to the do 
> {}while() loop in particular.  Also, if the change you made in 
> i810_get_dma_address() is needed for the SiS chipset, then there would 
> appear to be a hardware bug that needs worked around.  However, I 
> think this is the wrong way to solve it.  In prog_dmabuf(), we set the 
> control bits on the device such that when it finishes the LVI, it's 
> *suppossed* to stop playback.  Now, the only way I can think of that 
> the CIV register would update as fast as you can read it in 
> i810_get_dma_address() is if at the end of LVI, the card went into a 
> loop and the CIV is essentially running from 0 to 31 and going round 
> and round without doing anything.  In that case, the proper fix may 
> actually be to go into the interrupt handler and on LVI interrupt with 
> count==0 (for playback) call stop_dac and do similarly for record.  
> This is because I suspect that we aren't ever getting the DCH Stopped 
> interrupt on the SiS card.  Of course, the DCH Stop interrupt causes 
> us to call stop_{dac,adc} as appropriate, so the call to stop_dac in 
> drain_dac is redundant if this code is working as advertised.  I 
> suspect that fixing things in the interrupt handler will make the 
> stop_dac call in drain_dac and the change in i810_get_dma_address() 
> both unnecessary.  Can you test that for me? 

1) Is the LVI interrupt supposed to arrive when the chip *starts* 
playing the last buffer?
2) Does SiS actually do it this way?

If your theory on why the registers are spinning is correct, and if we 
receive the LVI interrupt with too much latency, your code will still 
deadlock, Doug. (The LVI interrupt handler calls update_ptr first thing, 
which calls get_dma_address.) Furthermore, if this turns out to be the 
case, the LVI IRQ handler uses dmabuf->count to determine whether to 
call stop_dac, and needs to call update_ptr to update dmabuf->count... 
so an explicit stop_dac might be needed elsewhere.

Even if the LVI interrupt comes at the beginning of the buffer, those 
2048 bytes will play in 10.67 ms. Can we really guarantee that kind of 
latency?


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

* Re: i810_audio
  2002-01-08 16:31 i810_audio willy tarreau
@ 2002-01-08 19:58 ` Doug Ledford
  2002-01-08 23:03   ` i810_audio willy tarreau
                     ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Doug Ledford @ 2002-01-08 19:58 UTC (permalink / raw)
  To: willy tarreau; +Cc: Mario Mikocevic, linux-kernel

willy tarreau wrote:

>>i810_audio.c:747: `PI_OR' undeclared (first use in
>>
> this function)
> 
> Replace PI_OR with PO_SR. It compiled for me after
> that.
> But my system still hangs after close while Thomas
> Gschwidt's
> patch works OK.


Someone posted one of the DMA Overrun on write error messages to me, and 
that allowed me to see that on the SiS hardware we are getting garbage in 
the upper 3 bits of the LVI register (presumably because we read garbage 
from the upper 3 bits of the CIV register).  So, I've put a 0.15 version of 
my driver on my site that now bounds our LVI and CIV reads so that we mask 
out any possible garbage.  And, since writing garbage to LVI could keep the 
hardware going in loops forever and other sorts of bad things, it might 
solve your problem.  Please give it a try and let me know how it works.





-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
  2002-01-08 15:11             ` i810_audio Mario Mikocevic
  2002-01-08 19:21               ` i810_audio Doug Ledford
@ 2002-01-08 19:22               ` Nathan Bryant
  1 sibling, 0 replies; 35+ messages in thread
From: Nathan Bryant @ 2002-01-08 19:22 UTC (permalink / raw)
  To: Mario Mikocevic; +Cc: Doug Ledford, Thomas Gschwind, linux-kernel

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

Mario Mikocevic wrote:

>Hi,
>
>>OK, various clean ups made, and enough of the SiS code included that I think 
>>it should work, plus one change to the i810 interrupt handler that will 
>>(hopefully) render the other change you made to get_dma_addr and drain_dac 
>>unnecessary.  If people could please download and test the new 0.14 version 
>>of the driver on my site, I would appreciate it.
>>
>>http://people.redhat.com/dledford/i810_audio.c.gz
>>
>
>Hmmm, maybe way too much cleanups !? :)
>
Works fine for me with the attached patch. (artsd, mmap mode ok so far)

[-- Attachment #2: 14.diff --]
[-- Type: text/plain, Size: 626 bytes --]

--- i810_audio.c.14	Tue Jan  8 03:28:31 2002
+++ linux/drivers/sound/i810_audio.c	Tue Jan  8 14:01:26 2002
@@ -655,7 +655,6 @@
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned int civ, offset, port, port_picb, bytes = 2;
-	struct i810_channel *c;
 	
 	if (!dmabuf->enable)
 		return 0;
@@ -744,7 +743,7 @@
 	if(card->pci_id == PCI_DEVICE_ID_SI_7012)
 		outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB );
 	else
-		outb( inb(card->iobase + PO_SR), card->iobase + PI_OR );
+		outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
 	outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA);
 }
 

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

* Re: i810_audio
  2002-01-08 15:11             ` i810_audio Mario Mikocevic
@ 2002-01-08 19:21               ` Doug Ledford
  2002-01-08 19:22               ` i810_audio Nathan Bryant
  1 sibling, 0 replies; 35+ messages in thread
From: Doug Ledford @ 2002-01-08 19:21 UTC (permalink / raw)
  To: Mario Mikocevic; +Cc: Thomas Gschwind, Nathan Bryant, linux-kernel

Mario Mikocevic wrote:

> Hi,
> 
> 
>>OK, various clean ups made, and enough of the SiS code included that I think 
>>it should work, plus one change to the i810 interrupt handler that will 
>>(hopefully) render the other change you made to get_dma_addr and drain_dac 
>>unnecessary.  If people could please download and test the new 0.14 version 
>>of the driver on my site, I would appreciate it.
>>
>>http://people.redhat.com/dledford/i810_audio.c.gz
>>
> 
> Hmmm, maybe way too much cleanups !? :)
> 
> -->
> 
> i810_audio.c: In function `i810_get_dma_addr':
> i810_audio.c:658: warning: unused variable `c'
> i810_audio.c: In function `__stop_dac':
> i810_audio.c:747: `PI_OR' undeclared (first use in this function)
> i810_audio.c:747: (Each undeclared identifier is reported only once
> i810_audio.c:747: for each function it appears in.)
> make[2]: *** [i810_audio.o] Error 1
> make[1]: *** [_modsubdir_sound] Error 2
> make: *** [_mod_drivers] Error 2
> 


Sorry.  Version that compiles is now on my web site.


> ps
> 	just got a note from a friend that .13 has tendency to lockup with
> 	heavy network traffic in the same time, no oops, nothing, ..
> 
> 



-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
@ 2002-01-08 16:31 willy tarreau
  2002-01-08 19:58 ` i810_audio Doug Ledford
  0 siblings, 1 reply; 35+ messages in thread
From: willy tarreau @ 2002-01-08 16:31 UTC (permalink / raw)
  To: Mario Mikocevic; +Cc: linux-kernel

> i810_audio.c:747: `PI_OR' undeclared (first use in
this function)

Replace PI_OR with PO_SR. It compiled for me after
that.
But my system still hangs after close while Thomas
Gschwidt's
patch works OK.

Still investigating.
Willy


___________________________________________________________
Do You Yahoo!? -- Une adresse @yahoo.fr gratuite et en français !
Yahoo! Courrier : http://courrier.yahoo.fr

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

* Re: i810_audio
  2002-01-08  9:02           ` i810_audio Doug Ledford
@ 2002-01-08 15:11             ` Mario Mikocevic
  2002-01-08 19:21               ` i810_audio Doug Ledford
  2002-01-08 19:22               ` i810_audio Nathan Bryant
  2002-01-09 15:47             ` i810_audio Mario Mikocevic
  1 sibling, 2 replies; 35+ messages in thread
From: Mario Mikocevic @ 2002-01-08 15:11 UTC (permalink / raw)
  To: Doug Ledford; +Cc: Thomas Gschwind, Nathan Bryant, linux-kernel

Hi,

> OK, various clean ups made, and enough of the SiS code included that I think 
> it should work, plus one change to the i810 interrupt handler that will 
> (hopefully) render the other change you made to get_dma_addr and drain_dac 
> unnecessary.  If people could please download and test the new 0.14 version 
> of the driver on my site, I would appreciate it.
> 
> http://people.redhat.com/dledford/i810_audio.c.gz

Hmmm, maybe way too much cleanups !? :)

-->

i810_audio.c: In function `i810_get_dma_addr':
i810_audio.c:658: warning: unused variable `c'
i810_audio.c: In function `__stop_dac':
i810_audio.c:747: `PI_OR' undeclared (first use in this function)
i810_audio.c:747: (Each undeclared identifier is reported only once
i810_audio.c:747: for each function it appears in.)
make[2]: *** [i810_audio.o] Error 1
make[1]: *** [_modsubdir_sound] Error 2
make: *** [_mod_drivers] Error 2


ps
	just got a note from a friend that .13 has tendency to lockup with
	heavy network traffic in the same time, no oops, nothing, ..

-- 
Mario Mikočević (Mozgy)
mozgy at hinet dot hr
My favourite FUBAR ...

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

* Re: i810_audio
  2002-01-08  8:11         ` i810_audio Doug Ledford
@ 2002-01-08  9:02           ` Doug Ledford
  2002-01-08 15:11             ` i810_audio Mario Mikocevic
  2002-01-09 15:47             ` i810_audio Mario Mikocevic
  0 siblings, 2 replies; 35+ messages in thread
From: Doug Ledford @ 2002-01-08  9:02 UTC (permalink / raw)
  To: Doug Ledford; +Cc: Thomas Gschwind, Nathan Bryant, linux-kernel

Doug Ledford wrote:

> Doug Ledford wrote:
> 
> 
>>> Fixes it applies except for the SiS integration:
>>> * drain_dac
>>>   Use interruptible_sleep_on_timeout instead of schedule_timeout.
>>>   I think this is cleaner. 
>>
>>
>>
>>
>> This is fine.
> 
> 
> 
> Oops, this is one of the mistakes Ben pointed out to me. 
> interruptible_sleep_on_timeout(wait_queue head, timeout) overwrites the 
> wait queue that we've already added ourselves to.  schedule_timeout() 
> does the right thing here.  (interruptible_sleep_on_timeout doesn't 
> really buy us much of anything we care about either, so it's find to 
> stay with schedule_timeout)
> 
> 
> 
> 

OK, various clean ups made, and enough of the SiS code included that I think 
it should work, plus one change to the i810 interrupt handler that will 
(hopefully) render the other change you made to get_dma_addr and drain_dac 
unnecessary.  If people could please download and test the new 0.14 version 
of the driver on my site, I would appreciate it.

http://people.redhat.com/dledford/i810_audio.c.gz

-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
  2002-01-05  2:13 ` i810_audio Thomas Gschwind
  2002-01-07 19:32   ` i810_audio Nathan Bryant
  2002-01-07 23:12   ` i810_audio Nathan Bryant
@ 2002-01-08  8:22   ` Martin Dalecki
  2 siblings, 0 replies; 35+ messages in thread
From: Martin Dalecki @ 2002-01-08  8:22 UTC (permalink / raw)
  To: Thomas Gschwind; +Cc: linux-kernel, Nathan Bryant

Thomas Gschwind wrote:

>
>I have integrated the SiS patches into Doug's code.  Chances are that
>

I would just like to report a full sccess with the aforementioned patch.
Hardware is a SIS7012, on a SIS735 chipset board. Even artsd seems to 
work fine.



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

* Re: i810_audio
  2002-01-08  7:59       ` i810_audio Doug Ledford
@ 2002-01-08  8:11         ` Doug Ledford
  2002-01-08  9:02           ` i810_audio Doug Ledford
  2002-01-08 20:01         ` i810_audio Nathan Bryant
  1 sibling, 1 reply; 35+ messages in thread
From: Doug Ledford @ 2002-01-08  8:11 UTC (permalink / raw)
  To: Doug Ledford; +Cc: Thomas Gschwind, Nathan Bryant, linux-kernel

Doug Ledford wrote:


>> Fixes it applies except for the SiS integration:
>> * drain_dac
>>   Use interruptible_sleep_on_timeout instead of schedule_timeout.
>>   I think this is cleaner. 
> 
> 
> 
> This is fine.


Oops, this is one of the mistakes Ben pointed out to me. 
interruptible_sleep_on_timeout(wait_queue head, timeout) overwrites the wait 
queue that we've already added ourselves to.  schedule_timeout() does the 
right thing here.  (interruptible_sleep_on_timeout doesn't really buy us 
much of anything we care about either, so it's find to stay with 
schedule_timeout)




-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
  2002-01-07 23:32     ` i810_audio Doug Ledford
@ 2002-01-08  7:59       ` Doug Ledford
  2002-01-08  8:11         ` i810_audio Doug Ledford
  2002-01-08 20:01         ` i810_audio Nathan Bryant
  0 siblings, 2 replies; 35+ messages in thread
From: Doug Ledford @ 2002-01-08  7:59 UTC (permalink / raw)
  To: Thomas Gschwind; +Cc: Doug Ledford, Nathan Bryant, linux-kernel


Hi Thomas.  I've been looking over the patch and trying to evaluate the 
various bug fixes that you mention against the patch.  My comments are 
inline (and ignore the fact that I'm positive my cut and past job will 
mangle the patch itself, this is for comments, not for applying).  I'm also 
replying to your comments.


Tom wrote:

> Doug,
> 
> attched you can find the latest SiS7012 patch.  This one is relative
> to your i810_audio.c driver.
> 
> Fixes it applies except for the SiS integration:
> * drain_dac
>   Use interruptible_sleep_on_timeout instead of schedule_timeout.
>   I think this is cleaner. 


This is fine.

> Set the timeout to
>   (dmabuf->count * HZ * 2) / (dmabuf->rate * 4)
>   since we play dmabuf->rate*4 bytes per second (16bit samples stereo).


This is fine as well.


>   Added stop_dac at the end.  Otherwise the system gets locked up sometimes.


This is (I think) a red herring.  I would suspect that the lockup you are 
seeing is more likely to have something to do with the changes in 
i810_get_dma_address().  I'm referring to the change to the do {}while() 
loop in particular.  Also, if the change you made in i810_get_dma_address() 
is needed for the SiS chipset, then there would appear to be a hardware bug 
that needs worked around.  However, I think this is the wrong way to solve 
it.  In prog_dmabuf(), we set the control bits on the device such that when 
it finishes the LVI, it's *suppossed* to stop playback.  Now, the only way I 
can think of that the CIV register would update as fast as you can read it 
in i810_get_dma_address() is if at the end of LVI, the card went into a loop 
and the CIV is essentially running from 0 to 31 and going round and round 
without doing anything.  In that case, the proper fix may actually be to go 
into the interrupt handler and on LVI interrupt with count==0 (for playback) 
call stop_dac and do similarly for record.  This is because I suspect that 
we aren't ever getting the DCH Stopped interrupt on the SiS card.  Of 
course, the DCH Stop interrupt causes us to call stop_{dac,adc} as 
appropriate, so the call to stop_dac in drain_dac is redundant if this code 
is working as advertised.  I suspect that fixing things in the interrupt 
handler will make the stop_dac call in drain_dac and the change in 
i810_get_dma_address() both unnecessary.  Can you test that for me?

Something else you might want to check into on the SiS chipset.  I actually 
wish Intel had done the i810 with the SR and PICB registers swapped because 
you are suppossed to be able to do a 32bit read from the CIV register 
location and get CIV, LVI, and SR all in one go.  With the SiS that means 
you could get CIV, LVI, and PCIB all in one go, making the while loop in 
i810_get_dma_address totally unnecessary on the SiS chipset (and making a 
slight boost to performance as well) as long as the SiS also supports a 
32bit read from CIV.


> * i810_read, i810_write
>   Set the timeout to (dmabuf->size * HZ * 2) / (dmabuf->rate * 4)
>   since we record / play dmabuf->rate*4 bytes per second (16bit samples
>   stereo).


Again, fine.


> * SNDCTL_DSP_GETxPTR
>   Don't change the recording / playback buffer.  A detailed explanation
>   can be found within the patch


As commented on by Nathan, this isn't right.


> Please correct me, if any of the above is wrong.
> 
> Thomas
> 


Now, I've been talking with Ben LaHaise here at Red Hat and he pointed out 
to me that the wait queue handling in i810_read and likely also in 
i810_write are both wrong.  Ben and I are looking into that and I'll put out 
a new version of the file that fixes those (obvious) problems shortly.  When 
I've got my 0.14 version out, could you please repatch your stuff against it 
and look into the items I've mentioned here?








-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
  2002-01-07 23:12   ` i810_audio Nathan Bryant
@ 2002-01-07 23:32     ` Doug Ledford
  2002-01-08  7:59       ` i810_audio Doug Ledford
  0 siblings, 1 reply; 35+ messages in thread
From: Doug Ledford @ 2002-01-07 23:32 UTC (permalink / raw)
  To: Nathan Bryant; +Cc: Thomas Gschwind, linux-kernel

Nathan Bryant wrote:

> Thomas Gschwind wrote:
> 
>> I have integrated the SiS patches into Doug's code.  Chances are that
>> it works now also in combination with artsd.  Can anybody test this
>> please?  And report your success (or failure)?  In case it does not
>> work you might want to change the fragsize to dmabuf->userfragsize
>> inside the i810_poll function (line 1596).  If I use
>> dmabuf->userfragsize, however, I get loads of DMA buffer
>> over/underruns in combination with xmms.  According to the sepc, I
>> think dmabuf->fragsize should be as correct as dmabuf->userfragsize.
>> I have not found and minimum available space requirement in the poll
>> man page or the OSS documentation I have?
>>
> Can you elaborate a little more? What do the buffer underflow sound 
> like, does it stop playing (silence) for a very short period and then 
> start again, or does it write ahead too far and overrite data that's 
> currently playing, causing garbled or repeated sound? Your email to me 
> describing this scenario left me a little confused.


Actually, this is moot.  Making it user fragsize instead of userfragsize is 
the *wrong* thing to do.  They are not the same and the semantics are pretty 
clear cut.

fragsize: The actual size of each dma ring buffer, of which we *always* have 
32.  This is a hardware limitation.  It's used for various calculations, but 
is only ever set in prog_dmabuf().

userfragsize: The SETFRAGMENT ioctl allows the user to tell us how often 
they want interrupted with updates about output/input progress and what size 
block they expect that update to indicate.  So, when the user requests a 
userfragsize of 4k, they are saying "Tell me about output completion with 
every 4k block you complete".  They also tell us how many blocks they want 
allocated.  The only requirement we have is that userfragsize * 
userfragcount (can't remember the variable name I actually call this in the 
code) == fragsize * 32.  Then, we use userfragsize to tell us how often to 
program the ring buffers for completion interrupts.  For instance, if we 
have 32 buffers of 512 bytes and userfragsize is 4k and userfragcount is 4, 
then we would program every 8th ring buffer to deliver a completion 
interrupt.  That's the relationship between the two.  So, in the poll 
routine, we need to alert userspace when userfragsize is reached, not 
fragsize.  They might be the same, and they might not.


> The former would indicate simple scheduling latency--nothing that can be 
> done about that--and the latter might be a problem with i810_update_ptr 
> or get_free_write_space in your implementation. I haven't looked at your 
> code too much yet...
> 
> I assume you're using xmms with artsd. What is your artsd fragment size? 
> (Click on kde control center, go to sound/sound server/sound i/o)
> 
> I assume you're using the artsd default of 4096, which is larger than 
> the actual hardware chunk size of 2048. If your problem is nothing more 
> than latency-induced underruns, perhaps it would make more sense to use 
> MIN(userfragsize, fragsize) to determine the return status for 
> i810_poll. Doug, do you have any thoughts?


If the above doesn't answer any questions, then let me know and I'll 
elaborate further.


> A buffer overrun is not the same as an underflow, even when we're 
> talking about a ring buffer ;)
> 
>> Fixes I applied except for the SiS integration:
>> * drain_dac
>>  Use interruptible_sleep_on_timeout instead of schedule_timeout.
>>  I think this is cleaner.  Set the timeout to  (dmabuf->count * HZ * 
>> 2) / (dmabuf->rate * 4)
>>  since we play dmabuf->rate*4 bytes per second (16bit samples stereo).
>>  Added stop_dac at the end.  Otherwise the system gets locked up 
>> sometimes.
> 
> Some sort of fix in the drain_dac area is probably needed for the real 
> Intel chips too; with 0.13 I was seeing a drain_dac: dma_timeout printk 
> occasionally on my setup which I hadn't bothered to debug yet. Didn't 
> hurt the machine and I figured it was maybe just dropped or latent 
> interrupts, so I got lazy. ;)
> 
>> * i810_read, i810_write
>>  Set the timeout to (dmabuf->size * HZ * 2) / (dmabuf->rate * 4)
>>  since we record / play dmabuf->rate*4 bytes per second (16bit samples 
>>  stereo).
>>
> Does this solve a problem for your SiS chip? i810_write seemed to be 
> working fine on my setup. (Intel hardware.)
> 


The timeout in these areas is intentionally left over long.  I don't mind it 
being close to the actual real expect time of completion, but do make sure 
it's a few ticks past completion time or else you might race against the 
completion interrupt depending on where you are in the current timer tick cycle.




-- 

  Doug Ledford <dledford@redhat.com>  http://people.redhat.com/dledford
       Please check my web site for aic7xxx updates/answers before
                       e-mailing me about problems


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

* Re: i810_audio
  2002-01-05  2:13 ` i810_audio Thomas Gschwind
  2002-01-07 19:32   ` i810_audio Nathan Bryant
@ 2002-01-07 23:12   ` Nathan Bryant
  2002-01-07 23:32     ` i810_audio Doug Ledford
  2002-01-08  8:22   ` i810_audio Martin Dalecki
  2 siblings, 1 reply; 35+ messages in thread
From: Nathan Bryant @ 2002-01-07 23:12 UTC (permalink / raw)
  To: Thomas Gschwind, Doug Ledford; +Cc: linux-kernel

Thomas Gschwind wrote:

>I have integrated the SiS patches into Doug's code.  Chances are that
>it works now also in combination with artsd.  Can anybody test this
>please?  And report your success (or failure)?  In case it does not
>work you might want to change the fragsize to dmabuf->userfragsize
>inside the i810_poll function (line 1596).  If I use
>dmabuf->userfragsize, however, I get loads of DMA buffer
>over/underruns in combination with xmms.  According to the sepc, I
>think dmabuf->fragsize should be as correct as dmabuf->userfragsize.
>I have not found and minimum available space requirement in the poll
>man page or the OSS documentation I have?
>
Can you elaborate a little more? What do the buffer underflow sound 
like, does it stop playing (silence) for a very short period and then 
start again, or does it write ahead too far and overrite data that's 
currently playing, causing garbled or repeated sound? Your email to me 
describing this scenario left me a little confused.

The former would indicate simple scheduling latency--nothing that can be 
done about that--and the latter might be a problem with i810_update_ptr 
or get_free_write_space in your implementation. I haven't looked at your 
code too much yet...

I assume you're using xmms with artsd. What is your artsd fragment size? 
(Click on kde control center, go to sound/sound server/sound i/o)

I assume you're using the artsd default of 4096, which is larger than 
the actual hardware chunk size of 2048. If your problem is nothing more 
than latency-induced underruns, perhaps it would make more sense to use 
MIN(userfragsize, fragsize) to determine the return status for 
i810_poll. Doug, do you have any thoughts?

A buffer overrun is not the same as an underflow, even when we're 
talking about a ring buffer ;)

>Fixes I applied except for the SiS integration:
>* drain_dac
>  Use interruptible_sleep_on_timeout instead of schedule_timeout.
>  I think this is cleaner.  Set the timeout to 
>  (dmabuf->count * HZ * 2) / (dmabuf->rate * 4)
>  since we play dmabuf->rate*4 bytes per second (16bit samples stereo).
>  Added stop_dac at the end.  Otherwise the system gets locked up sometimes. 
>
Some sort of fix in the drain_dac area is probably needed for the real 
Intel chips too; with 0.13 I was seeing a drain_dac: dma_timeout printk 
occasionally on my setup which I hadn't bothered to debug yet. Didn't 
hurt the machine and I figured it was maybe just dropped or latent 
interrupts, so I got lazy. ;)

>* i810_read, i810_write
>  Set the timeout to (dmabuf->size * HZ * 2) / (dmabuf->rate * 4)
>  since we record / play dmabuf->rate*4 bytes per second (16bit samples 
>  stereo).
>
Does this solve a problem for your SiS chip? i810_write seemed to be 
working fine on my setup. (Intel hardware.)


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

* Re: i810_audio
  2002-01-05  2:13 ` i810_audio Thomas Gschwind
@ 2002-01-07 19:32   ` Nathan Bryant
  2002-01-07 23:12   ` i810_audio Nathan Bryant
  2002-01-08  8:22   ` i810_audio Martin Dalecki
  2 siblings, 0 replies; 35+ messages in thread
From: Nathan Bryant @ 2002-01-07 19:32 UTC (permalink / raw)
  To: Thomas Gschwind; +Cc: linux-kernel, Doug Ledford

Thomas Gschwind wrote:

>
>+		/* TG: can this possibly be correct? 
>+		 * according to OSS doc, cinfo.blocks should return the
>+		 * number of fragment transitions since the previous call
>+		 * to this ioctl. Shouldn't it be better to set 
>+		 * cinfo.blocks to 0 if we do not support this?
>+		 * Why would we want to update the playback pointer? This
>+		 * is not mentioned in the OSS doc!
>+		 * val==number_free_bytes, hence we increase swptr by val.
>+		 * This means that we set the buffer to full.  It is 
>+		 * likely, however, that the hwptr has increased since the 
>+		 * call to this ioctl, hence part of the buffer is being
>+		 * played twice!
>+		 */
>
It is correct. MMAP mode doesn't work without it, and we only care about 
this IOCTL during mmap mode. I think you'll find that it should have 
been setting blocks correctly in mmap mode, the api sequence look like 
this (pseudocode:)

open("/dev/dsp");
mmap(...);
// apps that use mmap mode (only Quake* AFAIK, maybe some other games) 
will write zeroes to fill mmap buffer at this point

ioctl(SNDCTL_DSP_SETTRIGGER, &PCM_ENABLE_OUTPUT); // this updates LVI to 
point to entire 64K buffer and starts DAC. card begins playing silence. 
at this point, dmabuf->count = 64K and begins counting down to zero.

ioctl(SNDCTL_DSP_GETOPTR, &foo); // Quake will call this right before it 
needs to actually start playing sound (several seconds after the initial 
SETTRIGGER call, long enough for the 64K of silence to be played and for 
the DAC to stop with DCH interrupt.) Since the game *needs* to have the 
current output pointer before it can start writing data to be played, we 
take this as a signal that it *intends to do so*, and update the LVI to 
play the next 64K of ring-buffer. Note that the mmap-api spec in OSS 
docs says that the hardware should be set to loop around the ring buffer 
*ad infinitum* in mmap mode, with no further input, so setting it to 
simply play the first 64K--intead of infinity--can't hurt. (We can't 
tell the hardware to loop around the buffer forever since there is no 
command to do so. The only other way to handle that would be to make the 
completition interrupt handlers take care of it, which is more error 
prone in my opinion, interrupts could be missed plus it pays to keep the 
interrupt handlers as simple as possible.) So we rely on the app calling 
GETOPTR often enough to keep the LVI chasing around the buffer - which 
is *has* to do anyway if it wants to play meaningful data. If it stops 
calling GETOPTR, that means it has frozen or slowed down to much to 
handle audio anyway. Neat solution, no? ;-)

Note that, after every call to GETOPTR, dmabuf->count gets set to 64K, 
and the returned value for blocks is based on dmabuf->count, so the 
semantics should actually be correct; we do:

cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->userfragsize;

initially after GETOPTR, count = 64K, so blocks = (64K - 64K) / 
fragsize; ( = 0, correct according to docs)
count winds down to zero, updated by i810_update_ptr: assume next call 
to GETOPTR occurs exactly 64K later:

blocks = (64K - 0) / userfragsize = the number of blocks played since 
last call

so, please put it back the way it was ;)

>
>+#ifndef USE_ORIGINAL_SNDCTL_DSP_GETxPTR_CODE
>+		cinfo.blocks = 0;
>+#else
>+		cinfo.blocks = val/dmabuf->userfragsize;
>+		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
>+			dmabuf->count += val;
>+			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
> 			__i810_update_lvi(state, 0);
> 		}
>+#endif
>



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

* Re: i810_audio
       [not found] <3C338217.1080207@allegientsystems.com>
@ 2002-01-05  2:13 ` Thomas Gschwind
  2002-01-07 19:32   ` i810_audio Nathan Bryant
                     ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Thomas Gschwind @ 2002-01-05  2:13 UTC (permalink / raw)
  To: linux-kernel; +Cc: Nathan Bryant

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

Hi everybody,

On Wed, Jan 02, 2002 at 04:56:39PM -0500, Nathan Bryant wrote:
> Can you have a look at Doug Ledford's 0.13 driver? this incorporates 
> most or all of the fixes you mentioned, except for SiS support, and some 
> other fixes; it hasn't been incorporated into the main kernel quite yet 
> because it needs more testing.

I have integrated the SiS patches into Doug's code.  Chances are that
it works now also in combination with artsd.  Can anybody test this
please?  And report your success (or failure)?  In case it does not
work you might want to change the fragsize to dmabuf->userfragsize
inside the i810_poll function (line 1596).  If I use
dmabuf->userfragsize, however, I get loads of DMA buffer
over/underruns in combination with xmms.  According to the sepc, I
think dmabuf->fragsize should be as correct as dmabuf->userfragsize.
I have not found and minimum available space requirement in the poll
man page or the OSS documentation I have?

The patch attached to this email is still relative to the
drivers/sound/i810_audio.c file from the 2.4.17 kernel distribution.
A patched i810_audio.c can be found at
http://www.infosys.tuwien.ac.at/Staff/tom/SiS7012/

Fixes I applied except for the SiS integration:
* drain_dac
  Use interruptible_sleep_on_timeout instead of schedule_timeout.
  I think this is cleaner.  Set the timeout to 
  (dmabuf->count * HZ * 2) / (dmabuf->rate * 4)
  since we play dmabuf->rate*4 bytes per second (16bit samples stereo).
  Added stop_dac at the end.  Otherwise the system gets locked up sometimes. 
* i810_read, i810_write
  Set the timeout to (dmabuf->size * HZ * 2) / (dmabuf->rate * 4)
  since we record / play dmabuf->rate*4 bytes per second (16bit samples 
  stereo).
* SNDCTL_DSP_GETxPTR
  Don't change the recording / playback buffer.  A detailed explanation 
  can be found within the patch

Please correct me, if any of the above is wrong. 

> i've put a lot of work into this driver and i'd like to see everyone 
> working on i810 code continue to work off a single base of source code 
> rather than ending up with yet another fork...

Thanks for the hint!  Never was my plan; just did not know that Doug
had more recent code than the code found in the standard kernel
distribution.

Thomas
-- 
Thomas Gschwind                      Email: tom@infosys.tuwien.ac.at
Technische Universität Wien
Argentinierstraße 8/E1841            Tel: +43 (1) 58801 ext. 18412
A-1040 Wien, Austria, Europe         Fax: +43 (1) 58801 ext. 18491

[-- Attachment #2: sis7012-020105.patch --]
[-- Type: text/plain, Size: 38126 bytes --]

--- i810_audio.c.orig	Fri Dec 28 01:41:26 2001
+++ i810_audio.c	Sat Jan  5 03:50:00 2002
@@ -2,6 +2,9 @@
  *	Intel i810 and friends ICH driver for Linux
  *	Alan Cox <alan@redhat.com>
  *
+ *	SiS7012 code by
+ *	Thomas Gschwind <tom@infosys.tuwien.ac.at>
+ *
  *  Built from:
  *	Low level code:  Zach Brown (original nonworking i810 OSS driver)
  *			 Jaroslav Kysela <perex@suse.cz> (working ALSA driver)
@@ -14,7 +17,7 @@
  *	Analog Devices (A major AC97 codec maker)
  *	Intel Corp  (you've probably heard of them already)
  *
- * AC97 clues and assistance provided by
+ *  AC97 clues and assistance provided by
  *	Analog Devices
  *	Zach 'Fufu' Brown
  *	Jeff Garzik
@@ -84,6 +87,7 @@
 #include <linux/smp_lock.h>
 #include <linux/ac97_codec.h>
 #include <linux/wrapper.h>
+#include <linux/compiler.h>
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
 
@@ -102,15 +106,19 @@
 #ifndef PCI_DEVICE_ID_INTEL_440MX
 #define PCI_DEVICE_ID_INTEL_440MX	0x7195
 #endif
+#ifndef PCI_DEVICE_ID_SI_7012
+#define PCI_DEVICE_ID_SI_7012	0x7012
+#endif
 
 static int ftsodell=0;
 static int strict_clocking=0;
-static unsigned int clocking=48000;
+static unsigned int clocking=0;
 static int spdif_locked=0;
 
 //#define DEBUG
 //#define DEBUG2
 //#define DEBUG_INTERRUPTS
+//#define DEBUG_MMAP
 
 #define ADC_RUNNING	1
 #define DAC_RUNNING	2
@@ -197,7 +205,7 @@
 #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
 
 
-#define DRIVER_VERSION "0.04"
+#define DRIVER_VERSION "0.14"
 
 /* magic numbers to protect our data structures */
 #define I810_CARD_MAGIC		0x5072696E /* "Prin" */
@@ -220,7 +228,8 @@
 	ICH82901AB,
 	INTEL440MX,
 	INTELICH2,
-	INTELICH3
+	INTELICH3,
+	SI7012
 };
 
 static char * card_names[] = {
@@ -228,7 +237,8 @@
 	"Intel ICH 82901AB",
 	"Intel 440MX",
 	"Intel ICH2",
-	"Intel ICH3"
+	"Intel ICH3",
+	"SiS 7012"
 };
 
 static struct pci_device_id i810_pci_tbl [] __initdata = {
@@ -242,6 +252,8 @@
 	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2},
 	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3,
 	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3},
+	{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012},
 	{0,}
 };
 
@@ -357,39 +369,17 @@
 	struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *);
 	struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *);
 	void (*free_pcm_channel)(struct i810_card *, int chan);
+
+	/* We have a *very* long init time possibly, so use this to block */
+	/* attempts to open our devices before we are ready (stops oops'es) */
+	int initializing;
 };
 
 static struct i810_card *devs = NULL;
 
 static int i810_open_mixdev(struct inode *inode, struct file *file);
-static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
-				unsigned long arg);
-
-static inline unsigned ld2(unsigned int x)
-{
-	unsigned r = 0;
-	
-	if (x >= 0x10000) {
-		x >>= 16;
-		r += 16;
-	}
-	if (x >= 0x100) {
-		x >>= 8;
-		r += 8;
-	}
-	if (x >= 0x10) {
-		x >>= 4;
-		r += 4;
-	}
-	if (x >= 4) {
-		x >>= 2;
-		r += 2;
-	}
-	if (x >= 2)
-		r++;
-	return r;
-}
-
+static int i810_ioctl_mixdev(struct inode *inode, struct file *file,
+			     unsigned int cmd, unsigned long arg);
 static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg);
 static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
 
@@ -666,47 +656,38 @@
 static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	unsigned int civ, offset;
-	struct i810_channel *c;
+	struct i810_card *card = state->card;
+	unsigned int civ, picb, tries=2;
+	int port, port_picb;
 	
 	if (!dmabuf->enable)
 		return 0;
+
 	if (rec)
-		c = dmabuf->read_channel;
+		port = card->iobase + dmabuf->read_channel->port;
 	else
-		c = dmabuf->write_channel;
+		port = card->iobase + dmabuf->write_channel->port;
+	/* picb and sr are swapped on SiS7012 */
+	if (card->pci_id == PCI_DEVICE_ID_SI_7012)
+		port_picb = port + OFF_SR;
+	else
+		port_picb = port + OFF_PICB;
+
 	do {
-		civ = inb(state->card->iobase+c->port+OFF_CIV);
-		offset = (civ + 1) * dmabuf->fragsize -
-			      2 * inw(state->card->iobase+c->port+OFF_PICB);
+		civ = inb(port+OFF_CIV);
+		picb = inw(port_picb);
 		/* CIV changed before we read PICB (very seldom) ?
 		 * then PICB was rubbish, so try again */
-	} while (civ != inb(state->card->iobase+c->port+OFF_CIV));
-		 
-	return offset;
-}
-
-//static void resync_dma_ptrs(struct i810_state *state, int rec)
-//{
-//	struct dmabuf *dmabuf = &state->dmabuf;
-//	struct i810_channel *c;
-//	int offset;
-//
-//	if(rec) {
-//		c = dmabuf->read_channel;
-//	} else {
-//		c = dmabuf->write_channel;
-//	}
-//	if(c==NULL)
-//		return;
-//	offset = inb(state->card->iobase+c->port+OFF_CIV);
-//	if(offset == inb(state->card->iobase+c->port+OFF_LVI))
-//		offset++;
-//	offset *= dmabuf->fragsize;
-//	
-//	dmabuf->hwptr=dmabuf->swptr = offset;
-//}
-	
+	} while (unlikely(civ != inb(port+OFF_CIV) && --tries));
+	if (!tries) picb=0;
+
+	/* SiS7012 counts bytes, not samples */
+	if (card->pci_id == PCI_DEVICE_ID_SI_7012)
+		return ((civ + 1) * dmabuf->fragsize - picb) % dmabuf->dmasize;
+	else
+		return ((civ + 1) * dmabuf->fragsize - 2 * picb) % dmabuf->dmasize;
+}
+
 /* Stop recording (lock held) */
 static inline void __stop_adc(struct i810_state *state)
 {
@@ -718,7 +699,10 @@
 	// wait for the card to acknowledge shutdown
 	while( inb(card->iobase + PI_CR) != 0 ) ;
 	// now clear any latent interrupt bits (like the halt bit)
-	outb( inb(card->iobase + PI_SR), card->iobase + PI_SR );
+	if (card->pci_id == PCI_DEVICE_ID_SI_7012)
+		outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB );
+	else
+		outb( inb(card->iobase + PI_SR), card->iobase + PI_SR );
 	outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA);
 }
 
@@ -732,21 +716,27 @@
 	spin_unlock_irqrestore(&card->lock, flags);
 }
 
-static void start_adc(struct i810_state *state)
+static inline void __start_adc(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	struct i810_card *card = state->card;
-	unsigned long flags;
 
 	if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable &&
 	    (dmabuf->trigger & PCM_ENABLE_INPUT)) {
-		spin_lock_irqsave(&card->lock, flags);
 		dmabuf->enable |= ADC_RUNNING;
-		outb((1<<4) | (1<<2) | 1, card->iobase + PI_CR);
-		spin_unlock_irqrestore(&card->lock, flags);
+		outb((1<<4) | (1<<2) | 1, state->card->iobase + PI_CR);
 	}
 }
 
+static void start_adc(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__start_adc(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
 /* stop playback (lock held) */
 static inline void __stop_dac(struct i810_state *state)
 {
@@ -758,7 +748,10 @@
 	// wait for the card to acknowledge shutdown
 	while( inb(card->iobase + PO_CR) != 0 ) ;
 	// now clear any latent interrupt bits (like the halt bit)
-	outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
+	if (card->pci_id == PCI_DEVICE_ID_SI_7012)
+		outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB );
+	else
+		outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
 	outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA);
 }
 
@@ -772,20 +765,25 @@
 	spin_unlock_irqrestore(&card->lock, flags);
 }	
 
-static void start_dac(struct i810_state *state)
+static inline void __start_dac(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	struct i810_card *card = state->card;
-	unsigned long flags;
 
 	if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&
 	    (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
-		spin_lock_irqsave(&card->lock, flags);
 		dmabuf->enable |= DAC_RUNNING;
-		outb((1<<4) | (1<<2) | 1, card->iobase + PO_CR);
-		spin_unlock_irqrestore(&card->lock, flags);
+		outb((1<<4) | (1<<2) | 1, state->card->iobase + PO_CR);
 	}
 }
+static void start_dac(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__start_dac(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
 
 #define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
 #define DMABUF_MINORDER 1
@@ -805,6 +803,8 @@
 		dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags;
 	size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
 
+	if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size)
+		return 0;
 	/* alloc enough to satisfy the oss params */
 	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
 		if ( (PAGE_SIZE<<order) > size )
@@ -873,14 +873,19 @@
 	dmabuf->swptr = dmabuf->hwptr = 0;
 	spin_unlock_irqrestore(&state->card->lock, flags);
 
-	/* allocate DMA buffer if not allocated yet */
-	if (dmabuf->rawbuf)
-		dealloc_dmabuf(state);
+	/* allocate DMA buffer, let alloc_dmabuf determine if we are already
+	 * allocated well enough or if we should replace the current buffer
+	 * (assuming one is already allocated, if it isn't, then allocate it).
+	 */
 	if ((ret = alloc_dmabuf(state)))
 		return ret;
 
 	/* FIXME: figure out all this OSS fragment stuff */
 	/* I did, it now does what it should according to the OSS API.  DL */
+	/* We may not have realloced our dmabuf, but the fragment size to
+	 * fragment number ratio may have changed, so go ahead and reprogram
+	 * things
+	 */
 	dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder;
 	dmabuf->numfrag = SG_LEN;
 	dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;
@@ -922,6 +927,8 @@
 			sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i);
 			// the card will always be doing 16bit stereo
 			sg->control=dmabuf->fragsamples;
+			if (state->card->pci_id == PCI_DEVICE_ID_SI_7012)
+				sg->control *= 2;
 			sg->control|=CON_BUFPAD;
 			// set us up to get IOC interrupts as often as needed to
 			// satisfy numfrag requirements, no more
@@ -935,7 +942,6 @@
 		outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
 		outb(0, state->card->iobase+c->port+OFF_CIV);
 		outb(0, state->card->iobase+c->port+OFF_LVI);
-		dmabuf->count = 0;
 
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
@@ -969,31 +975,34 @@
 	else
 		port += dmabuf->write_channel->port;
 
-	if(dmabuf->mapped) {
-		if(rec)
-			dmabuf->swptr = (dmabuf->hwptr + dmabuf->dmasize
-				       	- dmabuf->count) % dmabuf->dmasize;
-		else
-			dmabuf->swptr = (dmabuf->hwptr + dmabuf->count)
-			       		% dmabuf->dmasize;
-	}
-	/*
-	 * two special cases, count == 0 on write
-	 * means no data, and count == dmasize
-	 * means no data on read, handle appropriately
+	/* if we are currently stopped, then our CIV is actually set to our
+	 * *last* sg segment and we are ready to wrap to the next.  However,
+	 * if we set our LVI to the last sg segment, then it won't wrap to
+	 * the next sg segment, it won't even get a start.  So, instead, when
+	 * we are stopped, we set both the LVI value and also we increment
+	 * the CIV value to the next sg segment to be played so that when
+	 * we call start_{dac,adc}, things will operate properly
 	 */
-	if(!rec && dmabuf->count == 0) {
-		outb(inb(port+OFF_CIV),port+OFF_LVI);
-		return;
-	}
-	if(rec && dmabuf->count == dmabuf->dmasize) {
-		outb(inb(port+OFF_CIV),port+OFF_LVI);
-		return;
+	if (!dmabuf->enable && dmabuf->ready) {
+		if(rec && dmabuf->count < dmabuf->dmasize &&
+		   (dmabuf->trigger & PCM_ENABLE_INPUT))
+		{
+			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			__start_adc(state);
+			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
+		} else if (!rec && dmabuf->count &&
+			   (dmabuf->trigger & PCM_ENABLE_OUTPUT))
+		{
+			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			__start_dac(state);
+			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
+		}
 	}
+
 	/* swptr - 1 is the tail of our transfer */
 	x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize;
 	x /= dmabuf->fragsize;
-	outb(x&31, port+OFF_LVI);
+	outb(x, port+OFF_LVI);
 }
 
 static void i810_update_lvi(struct i810_state *state, int rec)
@@ -1020,7 +1029,9 @@
 		/* update hardware pointer */
 		hwptr = i810_get_dma_addr(state, 1);
 		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
-//		printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+		printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
 		dmabuf->hwptr = hwptr;
 		dmabuf->total_bytes += diff;
 		dmabuf->count += diff;
@@ -1043,7 +1054,9 @@
 		/* update hardware pointer */
 		hwptr = i810_get_dma_addr(state, 0);
 		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
-//		printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+		printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
 		dmabuf->hwptr = hwptr;
 		dmabuf->total_bytes += diff;
 		dmabuf->count -= diff;
@@ -1055,7 +1068,7 @@
 			if(inb(state->card->iobase + PO_CIV) !=
 			   inb(state->card->iobase + PO_LVI)) {
 				printk(KERN_WARNING "i810_audio: DMA overrun on write\n");
-				printk("i810_audio: CIV %d, LVI %d, hwptr %x, "
+				printk("i810_audio: CIV %d, LVI %d, hwptr %d, "
 					"count %d\n",
 					inb(state->card->iobase + PO_CIV),
 					inb(state->card->iobase + PO_LVI),
@@ -1068,7 +1081,43 @@
 	}
 }
 
-static int drain_dac(struct i810_state *state, int nonblock)
+static inline int i810_get_free_write_space(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int free;
+
+	i810_update_ptr(state);
+	// catch underruns during playback
+	if (dmabuf->count < 0) {
+		dmabuf->count = 0;
+		dmabuf->swptr = dmabuf->hwptr;
+	}
+	free = dmabuf->dmasize - dmabuf->count;
+	free -= (dmabuf->hwptr % dmabuf->fragsize);
+	if(free < 0)
+		return(0);
+	return(free);
+}
+
+static inline int i810_get_available_read_data(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int avail;
+
+	i810_update_ptr(state);
+	// catch overruns during record
+	if (dmabuf->count > dmabuf->dmasize) {
+		dmabuf->count = dmabuf->dmasize;
+		dmabuf->swptr = dmabuf->hwptr;
+	}
+	avail = dmabuf->count;
+	avail -= (dmabuf->hwptr % dmabuf->fragsize);
+	if(avail < 0)
+		return(0);
+	return(avail);
+}
+
+static int drain_dac(struct i810_state *state)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	struct dmabuf *dmabuf = &state->dmabuf;
@@ -1093,33 +1142,41 @@
 		if (count <= 0)
 			break;
 
-		if (signal_pending(current))
-			break;
-
+		/* 
+		 * This will make sure that our LVI is correct, that our
+		 * pointer is updated, and that the DAC is running.  We
+		 * have to force the setting of dmabuf->trigger to avoid
+		 * any possible deadlocks.
+		 */
+		dmabuf->trigger = PCM_ENABLE_OUTPUT;
 		i810_update_lvi(state,0);
-		if (dmabuf->enable != DAC_RUNNING)
-			start_dac(state);
 
-		if (nonblock) {
-			remove_wait_queue(&dmabuf->wait, &wait);
-			set_current_state(TASK_RUNNING);
-			return -EBUSY;
-		}
+		if (signal_pending(current))
+			break;
 
-		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
-		tmo >>= 1;
-		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
+		/*
+		 * set the timeout to exactly twice as long as it *should*
+		 * take for the DAC to drain the DMA buffer
+		 * TG: have to divide by (dmabuf->rate * 4) since
+		 * we play dmabuf->rate * 4 byte/sec?!?
+		 * TG: use interruptible_sleep_on_timeout this
+		 * automatically wakes us up at the end of playback
+		 * (interrupt) => wake_up
+		 */
+		tmo = (count * HZ * 2) / (dmabuf->rate * 4);
+		if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo ? tmo : 1) && tmo) {
 			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");
 			break;
 		}
 	}
+	set_current_state(TASK_RUNNING);
+	/* TG: call stop_dac, otherwise we might crash when the device
+	 * is closed at least when using a SiS7012.  
+	 */
 	stop_dac(state);
-	synchronize_irq();
 	remove_wait_queue(&dmabuf->wait, &wait);
-	set_current_state(TASK_RUNNING);
 	if (signal_pending(current))
 		return -ERESTARTSYS;
-
 	return 0;
 }
 
@@ -1152,12 +1209,20 @@
 		
 		port+=c->port;
 		
-		status = inw(port + OFF_SR);
+		if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+			status = inw(port + OFF_PICB);
+		else
+			status = inw(port + OFF_SR);
 #ifdef DEBUG_INTERRUPTS
 		printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);
 #endif
 		if(status & DMA_INT_COMPLETE)
 		{
+			/* only wake_up() waiters if this interrupt signals
+			 * us being beyond a userfragsize of data open or
+			 * available, and i810_update_ptr() does that for
+			 * us
+			 */
 			i810_update_ptr(state);
 #ifdef DEBUG_INTERRUPTS
 			printk("COMP %d ", dmabuf->hwptr /
@@ -1166,6 +1231,7 @@
 		}
 		if(status & DMA_INT_LVI)
 		{
+			/* wake_up() unconditionally on LVI */
 			i810_update_ptr(state);
 			wake_up(&dmabuf->wait);
 #ifdef DEBUG_INTERRUPTS
@@ -1174,7 +1240,9 @@
 		}
 		if(status & DMA_INT_DCH)
 		{
+			/* wake_up() unconditionally on DCH */
 			i810_update_ptr(state);
+			wake_up(&dmabuf->wait);
 			if(dmabuf->enable & DAC_RUNNING)
 				count = dmabuf->count;
 			else
@@ -1182,13 +1250,21 @@
 			if(count > 0) {
 				outb(inb(port+OFF_CR) | 1, port+OFF_CR);
 			} else {
+				if (dmabuf->enable & DAC_RUNNING)
+					__stop_dac(state);
+				if (dmabuf->enable & ADC_RUNNING)
+					__stop_adc(state);
+				dmabuf->enable = 0;
 				wake_up(&dmabuf->wait);
 #ifdef DEBUG_INTERRUPTS
 				printk("DCH - STOP ");
 #endif
 			}
 		}
-		outw(status & DMA_INT_MASK, port + OFF_SR);
+		if (card->pci_id == PCI_DEVICE_ID_SI_7012)
+			outw(status & DMA_INT_MASK, port + OFF_PICB);
+		else
+			outw(status & DMA_INT_MASK, port + OFF_SR);
 	}
 #ifdef DEBUG_INTERRUPTS
 	printk(")\n");
@@ -1254,7 +1330,6 @@
 		return ret;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
-	dmabuf->trigger &= ~PCM_ENABLE_OUTPUT;
 	ret = 0;
 
         add_wait_queue(&dmabuf->wait, &waita);
@@ -1271,10 +1346,7 @@
                         continue;
                 }
 		swptr = dmabuf->swptr;
-		if (dmabuf->count > dmabuf->dmasize) {
-			dmabuf->count = dmabuf->dmasize;
-		}
-		cnt = dmabuf->count - dmabuf->fragsize;
+		cnt = i810_get_available_read_data(state);
 		// this is to make the copy_to_user simpler below
 		if(cnt > (dmabuf->dmasize - swptr))
 			cnt = dmabuf->dmasize - swptr;
@@ -1284,18 +1356,30 @@
 			cnt = count;
 		if (cnt <= 0) {
 			unsigned long tmo;
-			if(!dmabuf->enable) {
-				dmabuf->trigger |= PCM_ENABLE_INPUT;
-				start_adc(state);
-			}
+			/*
+			 * Don't let us deadlock.  The ADC won't start if
+			 * dmabuf->trigger isn't set.  A call to SETTRIGGER
+			 * could have turned it off after we set it to on
+			 * previously.
+			 */
+			dmabuf->trigger = PCM_ENABLE_INPUT;
+			/*
+			 * This does three things.  Updates LVI to be correct,
+			 * makes sure the ADC is running, and updates the
+			 * hwptr.
+			 */
 			i810_update_lvi(state,1);
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
-				return ret;
+				goto done;
 			}
-			/* This isnt strictly right for the 810  but it'll do */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= 1;
+			/* Set the timeout to how long it would take to fill
+			 * two of our buffers.  If we haven't been woke up
+			 * by then, then we know something is wrong.
+			 * TG: have to divide by (dmabuf->rate * 4) since
+			 * we play dmabuf->rate * 4 byte/sec
+			 */
+			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
 			/* There are two situations when sleep_on_timeout returns, one is when
 			   the interrupt is serviced correctly and the process is waked up by
 			   ISR ON TIME. Another is when timeout is expired, which means that
@@ -1315,7 +1399,7 @@
 			}
 			if (signal_pending(current)) {
 				ret = ret ? ret : -ERESTARTSYS;
-				return ret;
+				goto done;
 			}
 			continue;
 		}
@@ -1342,8 +1426,6 @@
 		ret += cnt;
 	}
 	i810_update_lvi(state,1);
-	if(!(dmabuf->enable & ADC_RUNNING))
-		start_adc(state);
  done:
         set_current_state(TASK_RUNNING);
         remove_wait_queue(&dmabuf->wait, &waita);
@@ -1384,7 +1466,6 @@
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
-	dmabuf->trigger &= ~PCM_ENABLE_INPUT;
 	ret = 0;
 
         add_wait_queue(&dmabuf->wait, &waita);
@@ -1402,12 +1483,14 @@
                 }
 
 		swptr = dmabuf->swptr;
-		if (dmabuf->count < 0) {
-			dmabuf->count = 0;
-		}
-		cnt = dmabuf->dmasize - swptr;
-		if(cnt > (dmabuf->dmasize - dmabuf->count))
-			cnt = dmabuf->dmasize - dmabuf->count;
+		cnt = i810_get_free_write_space(state);
+		/* Bound the maximum size to how much we can copy to the
+		 * dma buffer before we hit the end.  If we have more to
+		 * copy then it will get done in a second pass of this
+		 * loop starting from the beginning of the buffer.
+		 */
+		if(cnt > (dmabuf->dmasize - swptr))
+			cnt = dmabuf->dmasize - swptr;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 #ifdef DEBUG2
@@ -1417,20 +1500,23 @@
 			cnt = count;
 		if (cnt <= 0) {
 			unsigned long tmo;
-			// There is data waiting to be played
+			/*
+			 * Force the trigger setting since we would
+			 * deadlock with it set any other way
+			 */
+			dmabuf->trigger = PCM_ENABLE_OUTPUT;
 			i810_update_lvi(state,0);
-			if(!dmabuf->enable && dmabuf->count) {
-				/* force the starting incase SETTRIGGER has been used */
-				/* to stop it, otherwise this is a deadlock situation */
-				dmabuf->trigger |= PCM_ENABLE_OUTPUT;
-				start_dac(state);
-			}
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
 				goto ret;
 			}
-			/* Not strictly correct but works */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 4);
+			/* Set the timeout to how long it would take to fill
+			 * two of our buffers.  If we haven't been woke up
+			 * by then, then we know something is wrong.
+			 * TG: have to divide by (dmabuf->rate * 4) since
+			 * we play dmabuf->rate * 4 byte/sec
+			 */
+			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
 			/* There are two situations when sleep_on_timeout returns, one is when
 			   the interrupt is serviced correctly and the process is waked up by
 			   ISR ON TIME. Another is when timeout is expired, which means that
@@ -1481,9 +1567,7 @@
 		memset(dmabuf->rawbuf + swptr, '\0', x);
 	}
 	i810_update_lvi(state,0);
-	if (!dmabuf->enable && dmabuf->count >= dmabuf->userfragsize)
-		start_dac(state);
- ret:
+ret:
         set_current_state(TASK_RUNNING);
         remove_wait_queue(&dmabuf->wait, &waita);
 
@@ -1497,27 +1581,32 @@
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned long flags;
 	unsigned int mask = 0;
+	int fragsize;
 
 	if(!dmabuf->ready)
 		return 0;
 	poll_wait(file, &dmabuf->wait, wait);
 	spin_lock_irqsave(&state->card->lock, flags);
-	i810_update_ptr(state);
-	if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) {
-		if (dmabuf->count >= (signed)dmabuf->fragsize)
+	/* TG: already called from i810_get_available_read_data and
+	 * i810_get_free_write_space */
+/*  	i810_update_ptr(state); */
+	/* TG: If we use userfragsize, xmms generates a DMA buffer
+	 * overrun every other second */
+	if (state->card->pci_id == PCI_DEVICE_ID_SI_7012)
+		fragsize=dmabuf->fragsize;
+	else
+		fragsize=dmabuf->userfragsize;
+	if (dmabuf->enable & ADC_RUNNING ||
+	    dmabuf->trigger & PCM_ENABLE_INPUT) {
+		if (i810_get_available_read_data(state) >= fragsize)
 			mask |= POLLIN | POLLRDNORM;
 	}
-	if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING) {
-		if (dmabuf->mapped) {
-			if (dmabuf->count >= (signed)dmabuf->fragsize)
-				mask |= POLLOUT | POLLWRNORM;
-		} else {
-			if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)
-				mask |= POLLOUT | POLLWRNORM;
-		}
+	if (dmabuf->enable & DAC_RUNNING ||
+	    dmabuf->trigger & PCM_ENABLE_OUTPUT) {
+		if (i810_get_free_write_space(state) >= fragsize)
+			mask |= POLLOUT | POLLWRNORM;
 	}
 	spin_unlock_irqrestore(&state->card->lock, flags);
-
 	return mask;
 }
 
@@ -1559,12 +1648,9 @@
 			     size, vma->vm_page_prot))
 		goto out;
 	dmabuf->mapped = 1;
-	if(vma->vm_flags & VM_WRITE)
-		dmabuf->count = dmabuf->dmasize;
-	else
-		dmabuf->count = 0;
+	dmabuf->trigger = 0;
 	ret = 0;
-#ifdef DEBUG
+#ifdef DEBUG_MMAP
 	printk("i810_audio: mmap'ed %ld bytes of data space\n", size);
 #endif
 out:
@@ -1575,16 +1661,15 @@
 static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct i810_channel *c = NULL;
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned long flags;
 	audio_buf_info abinfo;
 	count_info cinfo;
 	unsigned int i_glob_cnt;
-	int val = 0, mapped, ret;
+	int val = 0, ret;
 	struct ac97_codec *codec = state->card->ac97_codec[0];
 
-	mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
-		((file->f_mode & FMODE_READ) && dmabuf->mapped);
 #ifdef DEBUG
 	printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *(int *)arg : 0);
 #endif
@@ -1601,13 +1686,23 @@
 #ifdef DEBUG
 		printk("SNDCTL_DSP_RESET\n");
 #endif
-		/* FIXME: spin_lock ? */
+		spin_lock_irqsave(&state->card->lock, flags);
 		if (dmabuf->enable == DAC_RUNNING) {
-			stop_dac(state);
+			c = dmabuf->write_channel;
+			__stop_dac(state);
 		}
 		if (dmabuf->enable == ADC_RUNNING) {
-			stop_adc(state);
+			c = dmabuf->read_channel;
+			__stop_adc(state);
+		}
+		if (c != NULL) {
+			outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */
+			outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
+			outb(0, state->card->iobase+c->port+OFF_CIV);
+			outb(0, state->card->iobase+c->port+OFF_LVI);
 		}
+
+		spin_unlock_irqrestore(&state->card->lock, flags);
 		synchronize_irq();
 		dmabuf->ready = 0;
 		dmabuf->swptr = dmabuf->hwptr = 0;
@@ -1620,10 +1715,8 @@
 #endif
 		if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK)
 			return 0;
-		drain_dac(state, 0);
-		dmabuf->ready = 0;
-		dmabuf->swptr = dmabuf->hwptr = 0;
-		dmabuf->count = dmabuf->total_bytes = 0;
+		drain_dac(state);
+		dmabuf->total_bytes = 0;
 		return 0;
 
 	case SNDCTL_DSP_SPEED: /* set smaple rate */
@@ -1676,7 +1769,6 @@
 #endif
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-
 		if (dmabuf->enable & DAC_RUNNING) {
 			stop_dac(state);
 		}
@@ -1711,16 +1803,7 @@
 #endif
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-
-		switch ( val ) {
-			case AFMT_S16_LE:
-				break;
-			case AFMT_QUERY:
-			default:
-				val = AFMT_S16_LE;
-				break;
-		}
-		return put_user(val, (int *)arg);
+		return put_user(AFMT_S16_LE, (int *)arg);
 
 	case SNDCTL_DSP_CHANNELS:
 #ifdef DEBUG
@@ -1820,22 +1903,47 @@
 
 		dmabuf->ossfragsize = 1<<(val & 0xffff);
 		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
-		if (dmabuf->ossmaxfrags <= 4)
-			dmabuf->ossmaxfrags = 4;
-		else if (dmabuf->ossmaxfrags <= 8)
-			dmabuf->ossmaxfrags = 8;
-		else if (dmabuf->ossmaxfrags <= 16)
-			dmabuf->ossmaxfrags = 16;
-		else
-			dmabuf->ossmaxfrags = 32;
+		if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
+			return -EINVAL;
+		/*
+		 * Bound the frag size into our allowed range of 256 - 4096
+		 */
+		if (dmabuf->ossfragsize < 256)
+			dmabuf->ossfragsize = 256;
+		else if (dmabuf->ossfragsize > 4096)
+			dmabuf->ossfragsize = 4096;
+		/*
+		 * The numfrags could be something reasonable, or it could
+		 * be 0xffff meaning "Give me as much as possible".  So,
+		 * we check the numfrags * fragsize doesn't exceed our
+		 * 64k buffer limit, nor is it less than our 8k minimum.
+		 * If it fails either one of these checks, then adjust the
+		 * number of fragments, not the size of them.  It's OK if
+		 * our number of fragments doesn't equal 32 or anything
+		 * like our hardware based number now since we are using
+		 * a different frag count for the hardware.  Before we get
+		 * into this though, bound the maxfrags to avoid overflow
+		 * issues.  A reasonable bound would be 64k / 256 since our
+		 * maximum buffer size is 64k and our minimum frag size is
+		 * 256.  On the other end, our minimum buffer size is 8k and
+		 * our maximum frag size is 4k, so the lower bound should
+		 * be 2.
+		 */
+
+		if(dmabuf->ossmaxfrags > 256)
+			dmabuf->ossmaxfrags = 256;
+		else if (dmabuf->ossmaxfrags < 2)
+			dmabuf->ossmaxfrags = 2;
+
 		val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
-		if (val < 16384)
-			val = 16384;
-		if (val > 65536)
-			val = 65536;
-		dmabuf->ossmaxfrags = val/dmabuf->ossfragsize;
-		if(dmabuf->ossmaxfrags<4)
-			dmabuf->ossfragsize = val/4;
+		while (val < 8192) {
+		    val <<= 1;
+		    dmabuf->ossmaxfrags <<= 1;
+		}
+		while (val > 65536) {
+		    val >>= 1;
+		    dmabuf->ossmaxfrags >>= 1;
+		}
 		dmabuf->ready = 0;
 #ifdef DEBUG
 		printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
@@ -1853,13 +1961,13 @@
 		i810_update_ptr(state);
 		abinfo.fragsize = dmabuf->userfragsize;
 		abinfo.fragstotal = dmabuf->userfrags;
-		if(dmabuf->mapped)
-			abinfo.bytes = dmabuf->count;
-		else
-			abinfo.bytes = dmabuf->dmasize - dmabuf->fragsize - dmabuf->count;
+		if (dmabuf->mapped)
+ 			abinfo.bytes = dmabuf->dmasize;
+  		else
+ 			abinfo.bytes = i810_get_free_write_space(state);
 		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes,
 			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
 #endif
@@ -1871,17 +1979,34 @@
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
+		val = i810_get_free_write_space(state);
 		cinfo.bytes = dmabuf->total_bytes;
 		cinfo.ptr = dmabuf->hwptr;
-		cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->userfragsize;
-		if (dmabuf->mapped) {
-			dmabuf->count = (dmabuf->dmasize - 
-					 (dmabuf->count & (dmabuf->userfragsize-1)));
+		/* TG: can this possibly be correct? 
+		 * according to OSS doc, cinfo.blocks should return the
+		 * number of fragment transitions since the previous call
+		 * to this ioctl. Shouldn't it be better to set 
+		 * cinfo.blocks to 0 if we do not support this?
+		 * Why would we want to update the playback pointer? This
+		 * is not mentioned in the OSS doc!
+		 * val==number_free_bytes, hence we increase swptr by val.
+		 * This means that we set the buffer to full.  It is 
+		 * likely, however, that the hwptr has increased since the 
+		 * call to this ioctl, hence part of the buffer is being
+		 * played twice!
+		 */
+#ifndef USE_ORIGINAL_SNDCTL_DSP_GETxPTR_CODE
+		cinfo.blocks = 0;
+#else
+		cinfo.blocks = val/dmabuf->userfragsize;
+		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+			dmabuf->count += val;
+			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
 			__i810_update_lvi(state, 0);
 		}
+#endif
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
 			cinfo.blocks, cinfo.ptr, dmabuf->count);
 #endif
@@ -1893,13 +2018,12 @@
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
+		abinfo.bytes = i810_get_available_read_data(state);
 		abinfo.fragsize = dmabuf->userfragsize;
 		abinfo.fragstotal = dmabuf->userfrags;
-		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
 		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes,
 			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
 #endif
@@ -1911,16 +2035,21 @@
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
+		val = i810_get_available_read_data(state);
 		cinfo.bytes = dmabuf->total_bytes;
-		cinfo.blocks = dmabuf->count/dmabuf->userfragsize;
 		cinfo.ptr = dmabuf->hwptr;
+		/* TG: here, the same applies as for SNDCTL_DSP_GETOPTR */
+#ifndef USE_ORIGINAL_SNDCTL_DSP_GETxPTR_CODE
+		cinfo.blocks = 0;
+#else
+		cinfo.blocks = dmabuf->count/dmabuf->userfragsize;
 		if (dmabuf->mapped) {
 			dmabuf->count &= (dmabuf->userfragsize-1);
 			__i810_update_lvi(state, 1);
 		}
+#endif
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
 			cinfo.blocks, cinfo.ptr, dmabuf->count);
 #endif
@@ -1950,7 +2079,7 @@
 	case SNDCTL_DSP_SETTRIGGER:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
 #endif
 		if( !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
@@ -1960,7 +2089,7 @@
 			stop_dac(state);
 		}
 		dmabuf->trigger = val;
-		if(val & PCM_ENABLE_OUTPUT) {
+		if(val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) {
 			if (!dmabuf->write_channel) {
 				dmabuf->ready = 0;
 				dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
@@ -1970,13 +2099,18 @@
 			if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 				return ret;
 			if (dmabuf->mapped) {
-				dmabuf->count = dmabuf->dmasize;
-				i810_update_lvi(state,0);
-			}
-			if (!dmabuf->enable && dmabuf->count > dmabuf->userfragsize)
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_update_ptr(state);
+				dmabuf->count = 0;
+				dmabuf->swptr = dmabuf->hwptr;
+				dmabuf->count = i810_get_free_write_space(state);
+				dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
+				__i810_update_lvi(state, 0);
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			} else
 				start_dac(state);
 		}
-		if(val & PCM_ENABLE_INPUT) {
+		if(val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) {
 			if (!dmabuf->read_channel) {
 				dmabuf->ready = 0;
 				dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
@@ -1986,12 +2120,14 @@
 			if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
 				return ret;
 			if (dmabuf->mapped) {
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_update_ptr(state);
+				dmabuf->swptr = dmabuf->hwptr;
 				dmabuf->count = 0;
-				i810_update_lvi(state,1);
+				spin_unlock_irqrestore(&state->card->lock, flags);
 			}
-			if (!dmabuf->enable && dmabuf->count <
-			    (dmabuf->dmasize - dmabuf->userfragsize))
-				start_adc(state);
+			i810_update_lvi(state, 1);
+			start_adc(state);
 		}
 		return 0;
 
@@ -2195,7 +2331,11 @@
 
 	/* find an avaiable virtual channel (instance of /dev/dsp) */
 	while (card != NULL) {
-		for (i = 0; i < NR_HW_CH; i++) {
+		for (i = 0; i < 50 && card->initializing; i++) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/20);
+		}
+		for (i = 0; i < NR_HW_CH && !card->initializing; i++) {
 			if (card->states[i] == NULL) {
 				state = card->states[i] = (struct i810_state *)
 					kmalloc(sizeof(struct i810_state), GFP_KERNEL);
@@ -2229,8 +2369,8 @@
 			card->states[i] = NULL;;
 			return -EBUSY;
 		}
-		i810_set_adc_rate(state, 8000);
 		dmabuf->trigger |= PCM_ENABLE_INPUT;
+		i810_set_adc_rate(state, 8000);
 	}
 	if(file->f_mode & FMODE_WRITE) {
 		if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) {
@@ -2241,13 +2381,13 @@
 		/* Initialize to 8kHz?  What if we don't support 8kHz? */
 		/*  Let's change this to check for S/PDIF stuff */
 	
+		dmabuf->trigger |= PCM_ENABLE_OUTPUT;
 		if ( spdif_locked ) {
 			i810_set_dac_rate(state, spdif_locked);
 			i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked);
 		} else {
 			i810_set_dac_rate(state, 8000);
 		}
-		dmabuf->trigger |= PCM_ENABLE_OUTPUT;
 	}
 		
 	/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
@@ -2276,7 +2416,7 @@
 	/* stop DMA state machine and free DMA buffers/channels */
 	if(dmabuf->enable & DAC_RUNNING ||
 	   (dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) {
-		drain_dac(state,0);
+		drain_dac(state);
 	}
 	if(dmabuf->enable & ADC_RUNNING) {
 		stop_adc(state);
@@ -2344,13 +2484,18 @@
 	int minor = MINOR(inode->i_rdev);
 	struct i810_card *card = devs;
 
-	for (card = devs; card != NULL; card = card->next)
-		for (i = 0; i < NR_AC97; i++)
+	for (card = devs; card != NULL; card = card->next) {
+		for (i = 0; i < 50 && card->initializing; i++) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/20);
+		}
+		for (i = 0; i < NR_AC97 && !card->initializing; i++)
 			if (card->ac97_codec[i] != NULL &&
 			    card->ac97_codec[i]->dev_mixer == minor) {
 				file->private_data = card->ac97_codec[i];
 				return 0;
 			}
+	}
 	return -ENODEV;
 }
 
@@ -2696,6 +2841,7 @@
 	}
 	memset(card, 0, sizeof(*card));
 
+	card->initializing = 1;
 	card->iobase = pci_resource_start (pci_dev, 1);
 	card->ac97base = pci_resource_start (pci_dev, 0);
 	card->pci_dev = pci_dev;
@@ -2752,7 +2898,8 @@
 	}
 	pci_set_drvdata(pci_dev, card);
 
-	if(clocking == 48000) {
+	if(clocking == 0) {
+		clocking = 48000;
 		i810_configure_clocking();
 	}
 
@@ -2767,11 +2914,12 @@
 		if (card->ac97_codec[i] != NULL) {
 			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
 			kfree (card->ac97_codec[i]);
+			card->ac97_codec[i] = NULL;
 		}
 		kfree(card);
 		return -ENODEV;
 	}
-
+ 	card->initializing = 0;
 	return 0;
 }
 
@@ -2789,6 +2937,7 @@
 		if (card->ac97_codec[i] != NULL) {
 			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
 			kfree (card->ac97_codec[i]);
+			card->ac97_codec[i] = NULL;
 		}
 	unregister_sound_dsp(card->dev_audio);
 	kfree(card);
@@ -2957,9 +3106,6 @@
 	if(ftsodell != 0) {
 		printk("i810_audio: ftsodell is now a deprecated option.\n");
 	}
-	if(clocking == 48000) {
-		i810_configure_clocking();
-	}
 	if(spdif_locked > 0 ) {
 		if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) {
 			printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked);

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

end of thread, other threads:[~2002-11-13 12:02 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <3C3BFF98.9080309@redhat.com>
2002-01-09  9:08 ` i810_audio willy tarreau
2002-11-11 21:16 i810_audio Rus Foster
  -- strict thread matches above, loose matches on Subject: below --
2002-11-10 23:37 i810 audio Alan Cox
2002-11-12 23:06 ` Brian C. Huffman
2002-11-12 23:38   ` Alan Cox
2002-11-12 23:43     ` Doug Ledford
2002-11-13  0:04       ` Peter Kundrat
2002-11-13  0:43         ` Alan Cox
2002-11-13 12:08           ` Brian C. Huffman
2002-11-12 23:52     ` Peter Kundrat
2002-01-10 19:21 i810_audio reddog83
2002-01-11  0:33 ` i810_audio Doug Ledford
2002-01-10 18:59 i810_audio reddog83
2002-01-10  6:42 i810_audio reddog83
2002-01-09  7:09 i810_audio reddog83
2002-01-08 16:31 i810_audio willy tarreau
2002-01-08 19:58 ` i810_audio Doug Ledford
2002-01-08 23:03   ` i810_audio willy tarreau
2002-01-09  6:28   ` i810_audio Nick Papadonis
2002-01-09  7:16   ` i810_audio willy tarreau
     [not found] <3C338217.1080207@allegientsystems.com>
2002-01-05  2:13 ` i810_audio Thomas Gschwind
2002-01-07 19:32   ` i810_audio Nathan Bryant
2002-01-07 23:12   ` i810_audio Nathan Bryant
2002-01-07 23:32     ` i810_audio Doug Ledford
2002-01-08  7:59       ` i810_audio Doug Ledford
2002-01-08  8:11         ` i810_audio Doug Ledford
2002-01-08  9:02           ` i810_audio Doug Ledford
2002-01-08 15:11             ` i810_audio Mario Mikocevic
2002-01-08 19:21               ` i810_audio Doug Ledford
2002-01-08 19:22               ` i810_audio Nathan Bryant
2002-01-09 15:47             ` i810_audio Mario Mikocevic
2002-01-08 20:01         ` i810_audio Nathan Bryant
2002-01-08 20:15           ` i810_audio Doug Ledford
2002-01-08 20:23           ` i810_audio Nathan Bryant
2002-01-08  8:22   ` i810_audio Martin Dalecki

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).