patch-2.4.4 linux/drivers/sound/i810_audio.c

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

diff -u --recursive --new-file v2.4.3/linux/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
@@ -101,8 +101,11 @@
 #endif
 
 static int ftsodell=0;
+static int strict_clocking=0;
 static unsigned int clocking=48000;
 
+//#define DEBUG
+//#define DEBUG2
 
 #define ADC_RUNNING	1
 #define DAC_RUNNING	2
@@ -169,6 +172,7 @@
 #define DMA_INT_COMPLETE	(1<<3)  /* buffer read/write complete and ioc set */
 #define DMA_INT_LVI		(1<<2)  /* last valid done */
 #define DMA_INT_CELV		(1<<1)  /* last valid is current */
+#define DMA_INT_DCH		(1)	/* DMA Controller Halted (happens on LVI interrupts) */
 #define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI)
 
 /* interrupts for the whole chip */
@@ -183,7 +187,7 @@
 #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
 
 
-#define DRIVER_VERSION "0.01"
+#define DRIVER_VERSION "0.02"
 
 /* magic numbers to protect our data structures */
 #define I810_CARD_MAGIC		0x5072696E /* "Prin" */
@@ -247,7 +251,7 @@
 	struct dmabuf {
 		/* wave sample stuff */
 		unsigned int rate;
-		unsigned char fmt, enable;
+		unsigned char fmt, enable, trigger;
 
 		/* hardware channel */
 		struct i810_channel *read_channel;
@@ -277,10 +281,9 @@
 		/* OSS stuff */
 		unsigned mapped:1;
 		unsigned ready:1;
-		unsigned endcleared:1;
 		unsigned update_flag;
-		unsigned ossfragshift;
-		int ossmaxfrags;
+		unsigned ossfragsize;
+		unsigned ossmaxfrags;
 		unsigned subdivision;
 	} dmabuf;
 };
@@ -408,8 +411,11 @@
 	/*
 	 *	Adjust for misclocked crap
 	 */
-	 
 	rate = ( rate * clocking)/48000;
+	if(strict_clocking && rate < 8000) {
+		rate = 8000;
+		dmabuf->rate = (rate * 48000)/clocking;
+	}
 	
 	if(rate != i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE))
 	{
@@ -456,6 +462,10 @@
 	 */
 	 
 	rate = ( rate * clocking)/48000;
+	if(strict_clocking && rate < 8000) {
+		rate = 8000;
+		dmabuf->rate = (rate * 48000)/clocking;
+	}
 	
 	if(rate != i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE))
 	{
@@ -478,34 +488,10 @@
 	return dmabuf->rate;
 }
 
-/* prepare channel attributes for playback */ 
-static void i810_play_setup(struct i810_state *state)
-{
-//	struct dmabuf *dmabuf = &state->dmabuf;
-//	struct i810_channel *channel = dmabuf->channel;
-	/* Fixed format. .. */
-	//if (dmabuf->fmt & I810_FMT_16BIT)
-	//if (dmabuf->fmt & I810_FMT_STEREO)
-}
-
-/* prepare channel attributes for recording */
-static void i810_rec_setup(struct i810_state *state)
-{
-//	u16 w;
-//	struct i810_card *card = state->card;
-//	struct dmabuf *dmabuf = &state->dmabuf;
-//	struct i810_channel *channel = dmabuf->channel;
-
-	/* Enable AC-97 ADC (capture) */
-//	if (dmabuf->fmt & I810_FMT_16BIT) {
-//	if (dmabuf->fmt & I810_FMT_STEREO)
-}
-
-
 /* get current playback/recording dma buffer pointer (byte offset from LBA),
    called with spinlock held! */
    
-extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state)
+extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state, int rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned int civ, offset;
@@ -513,17 +499,13 @@
 	
 	if (!dmabuf->enable)
 		return 0;
-	if (dmabuf->enable & DAC_RUNNING)
-		c = dmabuf->write_channel;
-	else if (dmabuf->enable & ADC_RUNNING)
+	if (rec)
 		c = dmabuf->read_channel;
-	else {
-		printk("i810_audio: invalid dmabuf->enable state in get_dma_addr\n");
-		return 0;
-	}
+	else
+		c = dmabuf->write_channel;
 	do {
 		civ = inb(state->card->iobase+c->port+OFF_CIV);
-		offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -
+		offset = (civ + 1) * dmabuf->fragsize -
 			      2 * inw(state->card->iobase+c->port+OFF_PICB);
 		/* CIV changed before we read PICB (very seldom) ?
 		 * then PICB was rubbish, so try again */
@@ -532,22 +514,26 @@
 	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;
-	}
-	offset = inb(state->card->iobase+c->port+OFF_CIV);
-	offset *= (dmabuf->dmasize/SG_LEN);
-	
-	dmabuf->hwptr=dmabuf->swptr = 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;
+//}
 	
 /* Stop recording (lock held) */
 extern __inline__ void __stop_adc(struct i810_state *state)
@@ -575,12 +561,13 @@
 	struct i810_card *card = state->card;
 	unsigned long flags;
 
-	spin_lock_irqsave(&card->lock, flags);
-	if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) {
+	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);
+		outb((1<<4) | (1<<2) | 1, card->iobase + PI_CR);
+		spin_unlock_irqrestore(&card->lock, flags);
 	}
-	spin_unlock_irqrestore(&card->lock, flags);
 }
 
 /* stop playback (lock held) */
@@ -609,12 +596,13 @@
 	struct i810_card *card = state->card;
 	unsigned long flags;
 
-	spin_lock_irqsave(&card->lock, flags);
-	if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
+	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);
+		outb((1<<4) | (1<<2) | 1, card->iobase + PO_CR);
+		spin_unlock_irqrestore(&card->lock, flags);
 	}
-	spin_unlock_irqrestore(&card->lock, flags);
 }
 
 #define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
@@ -625,18 +613,29 @@
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	void *rawbuf= NULL;
-	int order;
+	int order, size;
 	struct page *page, *pend;
 
-	/* alloc as big a chunk as we can, FIXME: is this necessary ?? */
-	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+	/* If we don't have any oss frag params, then use our default ones */
+	if(dmabuf->ossmaxfrags == 0)
+		dmabuf->ossmaxfrags = 4;
+	if(dmabuf->ossfragsize == 0)
+		dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags;
+	size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
+
+	/* alloc enough to satisfy the oss params */
+	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
+		if ( (PAGE_SIZE<<order) > size )
+			continue;
 		if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,
 						   PAGE_SIZE << order,
 						   &dmabuf->dma_handle)))
 			break;
+	}
 	if (!rawbuf)
 		return -ENOMEM;
 
+
 #ifdef DEBUG
 	printk("i810_audio: allocated %ld (order = %d) bytes at %p\n",
 	       PAGE_SIZE << order, order, rawbuf);
@@ -677,52 +676,53 @@
 	struct dmabuf *dmabuf = &state->dmabuf;
 	struct i810_channel *c;
 	struct sg_item *sg;
-	unsigned bytepersec;
-	unsigned bufsize;
 	unsigned long flags;
 	int ret;
-	unsigned fragsize;
+	unsigned fragint;
 	int i;
 
 	spin_lock_irqsave(&state->card->lock, flags);
-	resync_dma_ptrs(state, rec);
+	if(dmabuf->enable & DAC_RUNNING)
+		__stop_dac(state);
+	if(dmabuf->enable & ADC_RUNNING)
+		__stop_adc(state);
 	dmabuf->total_bytes = 0;
 	dmabuf->count = dmabuf->error = 0;
+	dmabuf->swptr = dmabuf->hwptr = 0;
 	spin_unlock_irqrestore(&state->card->lock, flags);
 
 	/* allocate DMA buffer if not allocated yet */
-	if (!dmabuf->rawbuf)
-		if ((ret = alloc_dmabuf(state)))
-			return ret;
+	if (dmabuf->rawbuf)
+		dealloc_dmabuf(state);
+	if ((ret = alloc_dmabuf(state)))
+		return ret;
 
 	/* FIXME: figure out all this OSS fragment stuff */
-	/* sample_shift is for 16 byte samples, add an extra shift for bytes */
-	bytepersec = dmabuf->rate << (sample_shift[dmabuf->fmt] + 1);
-	bufsize = PAGE_SIZE << dmabuf->buforder;
-	if (dmabuf->ossfragshift) {
-		if ((1000 << dmabuf->ossfragshift) < bytepersec)
-			dmabuf->fragshift = ld2(bytepersec/1000);
-		else
-			dmabuf->fragshift = dmabuf->ossfragshift;
+	/* I did, it now does what it should according to the OSS API.  DL */
+	dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder;
+	dmabuf->numfrag = SG_LEN;
+	dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;
+	dmabuf->fragsamples = dmabuf->fragsize >> 1;
+
+	memset(dmabuf->rawbuf, 0, dmabuf->dmasize);
+
+	if(dmabuf->ossmaxfrags == 4) {
+		fragint = 8;
+		dmabuf->ossfragsize = dmabuf->dmasize>>2;
+		dmabuf->fragshift = 2;
+	} else if (dmabuf->ossmaxfrags == 8) {
+		fragint = 4;
+		dmabuf->ossfragsize = dmabuf->dmasize>>3;
+		dmabuf->fragshift = 3;
+	} else if (dmabuf->ossmaxfrags == 16) {
+		fragint = 2;
+		dmabuf->ossfragsize = dmabuf->dmasize>>4;
+		dmabuf->fragshift = 4;
 	} else {
-		/* lets hand out reasonable big ass buffers by default */
-		dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
+		fragint = 1;
+		dmabuf->ossfragsize = dmabuf->dmasize>>5;
+		dmabuf->fragshift = 5;
 	}
-	dmabuf->numfrag = bufsize >> dmabuf->fragshift;
-	while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
-		dmabuf->fragshift--;
-		dmabuf->numfrag = bufsize >> dmabuf->fragshift;
-	}
-	dmabuf->fragsize = 1 << dmabuf->fragshift;
-	if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)
-		dmabuf->numfrag = dmabuf->ossmaxfrags;
-	dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];
-	dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;
-
-	memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80,
-	       dmabuf->dmasize);
-
-	fragsize = bufsize / SG_LEN;
 	/*
 	 *	Now set up the ring 
 	 */
@@ -737,24 +737,26 @@
 		 *	way (we might want more interrupts later..) 
 		 */
 	  
-		for(i=0;i<32;i++)
+		for(i=0;i<dmabuf->numfrag;i++)
 		{
-			sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i);
-			sg->control=(fragsize>>sample_shift[dmabuf->fmt]);
-			sg->control|=CON_IOC;
+			sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i);
+			// the card will always be doing 16bit stereo
+			sg->control=dmabuf->fragsamples;
+			sg->control|=CON_BUFPAD;
+			// set us up to get IOC interrupts as often as needed to
+			// satisfy numfrag requirements, no more
+			if( ((i+1) % fragint) == 0) {
+				sg->control|=CON_IOC;
+			}
 			sg++;
 		}
 		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(31, state->card->iobase+c->port+OFF_LVI);
 		outb(0, state->card->iobase+c->port+OFF_CIV);
+		outb(0, state->card->iobase+c->port+OFF_LVI);
+		dmabuf->count = 0;
 
-		if (c == dmabuf->read_channel) {
-			i810_rec_setup(state);
-		} else {
-			i810_play_setup(state);
-		}
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 		if(c != dmabuf->write_channel)
@@ -775,36 +777,115 @@
 
 	return 0;
 }
-/*
- * Clear the rest of the last i810 dma buffer, normally there is no rest
- * because the OSS fragment size is the same as the size of this buffer.
- */
-static void i810_clear_tail(struct i810_state *state)
+
+static void __i810_update_lvi(struct i810_state *state, int rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	unsigned swptr;
-	unsigned char silence = (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80;
-	unsigned int len;
-	unsigned long flags;
+	int x, port;
+	
+	port = state->card->iobase;
+	if(rec)
+		port += dmabuf->read_channel->port;
+	else
+		port += dmabuf->write_channel->port;
 
-	spin_lock_irqsave(&state->card->lock, flags);
-	swptr = dmabuf->swptr;
-	spin_unlock_irqrestore(&state->card->lock, flags);
+	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(!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;
+	}
+	/* 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);
+}
 
-	if(dmabuf->dmasize)
-		len = swptr % (dmabuf->dmasize/SG_LEN);
-	else
-		len = 0;
-	
-	memset(dmabuf->rawbuf + swptr, silence, len);
+static void i810_update_lvi(struct i810_state *state, int rec)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned long flags;
 
+	if(!dmabuf->ready)
+		return;
 	spin_lock_irqsave(&state->card->lock, flags);
-	dmabuf->swptr += len;
-	dmabuf->count += len;
+	__i810_update_lvi(state, rec);
 	spin_unlock_irqrestore(&state->card->lock, flags);
+}
 
-	/* restart the dma machine in case it is halted */
-	start_dac(state);
+/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
+static void i810_update_ptr(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	unsigned hwptr;
+	int diff;
+
+	/* error handling and process wake up for DAC */
+	if (dmabuf->enable == ADC_RUNNING) {
+		/* 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);
+		dmabuf->hwptr = hwptr;
+		dmabuf->total_bytes += diff;
+		dmabuf->count += diff;
+		if (dmabuf->count > dmabuf->dmasize) {
+			/* buffer underrun or buffer overrun */
+			/* this is normal for the end of a read */
+			/* only give an error if we went past the */
+			/* last valid sg entry */
+			if(inb(state->card->iobase + PI_CIV) !=
+			   inb(state->card->iobase + PI_LVI)) {
+				printk(KERN_WARNING "i810_audio: DMA overrun on read\n");
+				dmabuf->error++;
+			}
+		}
+		if (dmabuf->count > dmabuf->ossfragsize)
+			wake_up(&dmabuf->wait);
+	}
+	/* error handling and process wake up for DAC */
+	if (dmabuf->enable == DAC_RUNNING) {
+		/* 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);
+		dmabuf->hwptr = hwptr;
+		dmabuf->total_bytes += diff;
+		dmabuf->count -= diff;
+		if (dmabuf->count < 0) {
+			/* buffer underrun or buffer overrun */
+			/* this is normal for the end of a write */
+			/* only give an error if we went past the */
+			/* last valid sg entry */
+			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, "
+					"count %d\n",
+					inb(state->card->iobase + PO_CIV),
+					inb(state->card->iobase + PO_LVI),
+					dmabuf->hwptr, dmabuf->count);
+				dmabuf->error++;
+			}
+		}
+		if (dmabuf->count < (dmabuf->dmasize-dmabuf->ossfragsize))
+			wake_up(&dmabuf->wait);
+	}
 }
 
 static int drain_dac(struct i810_state *state, int nonblock)
@@ -815,7 +896,7 @@
 	unsigned long tmo;
 	int count;
 
-	if (dmabuf->mapped || !dmabuf->ready)
+	if (!dmabuf->ready)
 		return 0;
 
 	add_wait_queue(&dmabuf->wait, &wait);
@@ -825,6 +906,7 @@
 		current->state = TASK_INTERRUPTIBLE;
 
 		spin_lock_irqsave(&state->card->lock, flags);
+		i810_update_ptr(state);
 		count = dmabuf->count;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
@@ -834,6 +916,10 @@
 		if (signal_pending(current))
 			break;
 
+		i810_update_lvi(state,0);
+		if (dmabuf->enable != DAC_RUNNING)
+			start_dac(state);
+
 		if (nonblock) {
 			remove_wait_queue(&dmabuf->wait, &wait);
 			current->state = TASK_RUNNING;
@@ -841,7 +927,7 @@
 		}
 
 		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
-		tmo >>= sample_shift[dmabuf->fmt];
+		tmo >>= 1;
 		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
 			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");
 			break;
@@ -855,88 +941,18 @@
 	return 0;
 }
 
-/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */
-static void i810_update_ptr(struct i810_state *state)
-{
-	struct dmabuf *dmabuf = &state->dmabuf;
-	unsigned hwptr, swptr;
-	int clear_cnt = 0;
-	int diff;
-	unsigned char silence;
-//	unsigned half_dmasize;
-
-	/* update hardware pointer */
-	hwptr = i810_get_dma_addr(state);
-	diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
-//	printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
-	dmabuf->hwptr = hwptr;
-	dmabuf->total_bytes += diff;
-
-	/* error handling and process wake up for DAC */
-	if (dmabuf->enable == ADC_RUNNING) {
-		if (dmabuf->mapped) {
-			dmabuf->count -= diff;
-			if (dmabuf->count >= (signed)dmabuf->fragsize)
-				wake_up(&dmabuf->wait);
-		} else {
-			dmabuf->count += diff;
-
-			if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
-				/* buffer underrun or buffer overrun, we have no way to recover
-				   it here, just stop the machine and let the process force hwptr
-				   and swptr to sync */
-				__stop_adc(state);
-				dmabuf->error++;
-			}
-			else if (!dmabuf->endcleared) {
-				swptr = dmabuf->swptr;
-				silence = (dmabuf->fmt & I810_FMT_16BIT ? 0 : 0x80);
-				if (dmabuf->count < (signed) dmabuf->fragsize) 
-				{
-					clear_cnt = dmabuf->fragsize;
-					if ((swptr + clear_cnt) > dmabuf->dmasize)
-						clear_cnt = dmabuf->dmasize - swptr;
-					memset (dmabuf->rawbuf + swptr, silence, clear_cnt);
-					dmabuf->endcleared = 1;
-				}
-			}			
-			if (dmabuf->count < (signed)dmabuf->dmasize/2) {
-				wake_up(&dmabuf->wait);
-			}
-		}
-	}
-	/* error handling and process wake up for DAC */
-	if (dmabuf->enable == DAC_RUNNING) {
-		if (dmabuf->mapped) {
-			dmabuf->count += diff;
-			if (dmabuf->count >= (signed)dmabuf->fragsize)
-				wake_up(&dmabuf->wait);
-		} else {
-			dmabuf->count -= diff;
-
-			if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) {
-				/* buffer underrun or buffer overrun, we have no way to recover
-				   it here, just stop the machine and let the process force hwptr
-				   and swptr to sync */
-				__stop_dac(state);
-				printk(KERN_WARNING "i810_audio: DMA overrun on send\n");
-				dmabuf->error++;
-			}
-			if (dmabuf->count < (signed)dmabuf->dmasize/2) {
-				wake_up(&dmabuf->wait);
-			}
-		}
-	}
-}
-
 static void i810_channel_interrupt(struct i810_card *card)
 {
-	int i;
-
+	int i, count;
+	
+#ifdef DEBUG_INTERRUPTS
+	printk("CHANNEL ");
+#endif
 	for(i=0;i<NR_HW_CH;i++)
 	{
 		struct i810_state *state = card->states[i];
 		struct i810_channel *c;
+		struct dmabuf *dmabuf;
 		unsigned long port = card->iobase;
 		u16 status;
 		
@@ -944,35 +960,56 @@
 			continue;
 		if(!state->dmabuf.ready)
 			continue;
-		if(state->dmabuf.enable & DAC_RUNNING)
-			c=state->dmabuf.write_channel;
+		dmabuf = &state->dmabuf;
+		if(dmabuf->enable & DAC_RUNNING)
+			c=dmabuf->write_channel;
 		else
-			c=state->dmabuf.read_channel;
+			c=dmabuf->read_channel;
 		
 		port+=c->port;
 		
 		status = inw(port + OFF_SR);
-		
+#ifdef DEBUG_INTERRUPTS
+		printk("NUM %d PORT %lX IRQ ( ST%d ", c->num, c->port, status);
+#endif
 		if(status & DMA_INT_COMPLETE)
 		{
-			int x;
-			/* Keep the card chasing its tail */
-			outb(x=((inb(port+OFF_CIV)-1)&31), port+OFF_LVI);
 			i810_update_ptr(state);
+#ifdef DEBUG_INTERRUPTS
+			printk("COMP%d ",x);
+#endif
 		}
 		if(status & DMA_INT_LVI)
 		{
-			/* Back to the start */
 			i810_update_ptr(state);
-			outb(0, port + OFF_CR);
+			wake_up(&dmabuf->wait);
+#ifdef DEBUG_INTERRUPTS
+			printk("LVI ");
+#endif
+		}
+		if(status & DMA_INT_DCH)
+		{
+			i810_update_ptr(state);
+			if(dmabuf->enable & DAC_RUNNING)
+				count = dmabuf->count;
+			else
+				count = dmabuf->dmasize - dmabuf->count;
+			if(count > 0) {
+				outb(inb(port+OFF_CR) | 1, port+OFF_CR);
+			} else {
+				wake_up(&dmabuf->wait);
+#ifdef DEBUG_INTERRUPTS
+				printk("DCH - STOP ");
+#endif
+			}
 		}
 		outw(status & DMA_INT_MASK, port + OFF_SR);
 	}
+#ifdef DEBUG_INTERRUPTS
+	printk(")\n");
+#endif
 }
 
-static u32 jiff = 0;
-static u32 jiff_count = 0;
-
 static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct i810_card *card = (struct i810_card *)dev_id;
@@ -1009,10 +1046,10 @@
 	struct dmabuf *dmabuf = &state->dmabuf;
 	ssize_t ret;
 	unsigned long flags;
-	unsigned swptr;
+	unsigned int swptr;
 	int cnt;
 
-#ifdef DEBUG
+#ifdef DEBUG2
 	printk("i810_audio: i810_read called, count = %d\n", count);
 #endif
 
@@ -1026,43 +1063,45 @@
 		dmabuf->ready = 0;
 		dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
 		if (!dmabuf->read_channel) {
-			return -ENODEV;
+			return -EBUSY;
 		}
 	}
 	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
 		return ret;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
+	dmabuf->trigger &= ~PCM_ENABLE_OUTPUT;
 	ret = 0;
 
 	while (count > 0) {
 		spin_lock_irqsave(&state->card->lock, flags);
-		if (dmabuf->count > (signed) dmabuf->dmasize) {
-			/* buffer overrun, we are recovering from sleep_on_timeout,
-			   resync hwptr and swptr, make process flush the buffer */
-			dmabuf->count = dmabuf->dmasize;
-			dmabuf->swptr = dmabuf->hwptr;
-		}
 		swptr = dmabuf->swptr;
-		cnt = dmabuf->dmasize - swptr;
-		if (dmabuf->count < cnt)
-			cnt = dmabuf->count;
+		if (dmabuf->count > dmabuf->dmasize) {
+			dmabuf->count = 0;
+		}
+		cnt = dmabuf->count;
+		if(cnt > (dmabuf->dmasize - swptr))
+			cnt = dmabuf->dmasize - swptr;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			unsigned long tmo;
-			/* buffer is empty, start the dma machine and wait for data to be
-			   recorded */
-			start_adc(state);
+			// are we already running?  only start us if we aren't running
+			// currently
+			i810_update_lvi(state,1);
+			if(!dmabuf->enable) {
+				dmabuf->trigger |= PCM_ENABLE_INPUT;
+				start_adc(state);
+			}
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
 				return ret;
 			}
 			/* This isnt strictly right for the 810  but it'll do */
 			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
+			tmo >>= 1;
 			/* 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
@@ -1102,8 +1141,9 @@
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
-		start_adc(state);
 	}
+	i810_update_lvi(state,1);
+	start_adc(state);
 	return ret;
 }
 
@@ -1115,10 +1155,10 @@
 	struct dmabuf *dmabuf = &state->dmabuf;
 	ssize_t ret;
 	unsigned long flags;
-	unsigned swptr;
-	int cnt;
+	unsigned int swptr = 0;
+	int cnt, x;
 
-#ifdef DEBUG
+#ifdef DEBUG2
 	printk("i810_audio: i810_write called, count = %d\n", count);
 #endif
 
@@ -1132,25 +1172,23 @@
 		dmabuf->ready = 0;
 		dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
 		if(!dmabuf->write_channel)
-			return -ENODEV;
+			return -EBUSY;
 	}
 	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+	dmabuf->trigger &= ~PCM_ENABLE_INPUT;
 	ret = 0;
 
 	while (count > 0) {
 		spin_lock_irqsave(&state->card->lock, flags);
+		swptr = dmabuf->swptr;
 		if (dmabuf->count < 0) {
-			/* buffer underrun, we are recovering from sleep_on_timeout,
-			   resync hwptr and swptr */
 			dmabuf->count = 0;
-			dmabuf->swptr = dmabuf->hwptr;
 		}
-		swptr = dmabuf->swptr;
 		cnt = dmabuf->dmasize - swptr;
-		if (dmabuf->count + cnt > dmabuf->dmasize)
+		if(cnt > (dmabuf->dmasize - dmabuf->count))
 			cnt = dmabuf->dmasize - dmabuf->count;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
@@ -1158,16 +1196,20 @@
 			cnt = count;
 		if (cnt <= 0) {
 			unsigned long tmo;
-			/* buffer is full, start the dma machine and wait for data to be
-			   played */
-			start_dac(state);
+			// There is data waiting to be played
+			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;
 				return ret;
 			}
 			/* Not strictly correct but works */
-			tmo = dmabuf->rate << (sample_shift[dmabuf->fmt] + 1);
-			tmo = dmabuf->dmasize * HZ / tmo;
+			tmo = (dmabuf->dmasize * HZ) / (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
@@ -1184,6 +1226,7 @@
 #endif
 				/* a buffer underrun, we delay the recovery until next time the
 				   while loop begin and we REALLY have data to play */
+				//return ret;
 			}
 			if (signal_pending(current)) {
 				if (!ret) ret = -ERESTARTSYS;
@@ -1191,7 +1234,7 @@
 			}
 			continue;
 		}
-		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+		if (copy_from_user(dmabuf->rawbuf+swptr,buffer,cnt)) {
 			if (!ret) ret = -EFAULT;
 			return ret;
 		}
@@ -1201,14 +1244,21 @@
 		spin_lock_irqsave(&state->card->lock, flags);
 		dmabuf->swptr = swptr;
 		dmabuf->count += cnt;
-		dmabuf->endcleared = 0;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 		count -= cnt;
 		buffer += cnt;
 		ret += cnt;
-		start_dac(state);
 	}
+	if (swptr % dmabuf->fragsize) {
+		x = dmabuf->fragsize - (swptr % dmabuf->fragsize);
+		if((x + dmabuf->count) < dmabuf->dmasize)
+			memset(dmabuf->rawbuf + swptr, '\0', x);
+	}
+	i810_update_lvi(state,0);
+	if (!dmabuf->enable && dmabuf->count >= dmabuf->ossfragsize)
+		start_dac(state);
+
 	return ret;
 }
 
@@ -1220,21 +1270,9 @@
 	unsigned long flags;
 	unsigned int mask = 0;
 
-	if (file->f_mode & FMODE_WRITE) {
-		if (!dmabuf->write_channel)
-			return 0;
-		if (!dmabuf->ready && prog_dmabuf(state, 0))
-			return 0;
-		poll_wait(file, &dmabuf->wait, wait);
-	} else { 
-	// don't do both read and write paths or we won't get woke up properly
-	// when we have a file with both permissions
-		if (!dmabuf->read_channel)
-			return 0;
-		if (!dmabuf->ready && prog_dmabuf(state, 1))
-			return 0;
-		poll_wait(file, &dmabuf->wait, wait);
-	}
+	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) {
@@ -1262,22 +1300,24 @@
 	int ret = -EINVAL;
 	unsigned long size;
 
-	/* 
-	 *	Until we figure out a few problems
-	 */
-	 
 	lock_kernel();
 	if (vma->vm_flags & VM_WRITE) {
-		if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL)
-			goto out;
-		if ((ret = prog_dmabuf(state, 0)) != 0)
+		if (!dmabuf->write_channel &&
+		    (dmabuf->write_channel =
+		     state->card->alloc_pcm_channel(state->card)) == NULL) {
+			ret = -EBUSY;
 			goto out;
-	} else if (vma->vm_flags & VM_READ) {
-		if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL)
-			goto out;
-		if ((ret = prog_dmabuf(state, 1)) != 0)
+		}
+	}
+	if (vma->vm_flags & VM_READ) {
+		if (!dmabuf->read_channel &&
+		    (dmabuf->read_channel = 
+		     state->card->alloc_rec_pcm_channel(state->card)) == NULL) {
+			ret = -EBUSY;
 			goto out;
-	} else 
+		}
+	}
+	if ((ret = prog_dmabuf(state, 0)) != 0)
 		goto out;
 
 	ret = -EINVAL;
@@ -1291,9 +1331,13 @@
 			     size, vma->vm_page_prot))
 		goto out;
 	dmabuf->mapped = 1;
+	if(vma->vm_flags & VM_WRITE)
+		dmabuf->count = dmabuf->dmasize;
+	else
+		dmabuf->count = 0;
 	ret = 0;
 #ifdef DEBUG
-	printk("i810_audio: mmap'ed %d bytes of data space\n", size);
+	printk("i810_audio: mmap'ed %ld bytes of data space\n", size);
 #endif
 out:
 	unlock_kernel();
@@ -1312,41 +1356,52 @@
 	mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
 		((file->f_mode & FMODE_READ) && dmabuf->mapped);
 #ifdef DEBUG
-	printk("i810_audio: i810_ioctl, command = %2d, arg = 0x%08x\n",
-	       _IOC_NR(cmd), arg ? *(int *)arg : 0);
+	printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *(int *)arg : 0);
 #endif
 
 	switch (cmd) 
 	{
 	case OSS_GETVERSION:
+#ifdef DEBUG
+		printk("OSS_GETVERSION\n");
+#endif
 		return put_user(SOUND_VERSION, (int *)arg);
 
 	case SNDCTL_DSP_RESET:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_RESET\n");
+#endif
 		/* FIXME: spin_lock ? */
-		if (file->f_mode & FMODE_WRITE) {
+		if (dmabuf->enable == DAC_RUNNING) {
 			stop_dac(state);
-			synchronize_irq();
-			dmabuf->ready = 0;
-			resync_dma_ptrs(state, 0);
-			dmabuf->swptr = dmabuf->hwptr = 0;
-			dmabuf->count = dmabuf->total_bytes = 0;
 		}
-		if (file->f_mode & FMODE_READ) {
+		if (dmabuf->enable == ADC_RUNNING) {
 			stop_adc(state);
-			synchronize_irq();
-			resync_dma_ptrs(state, 1);
-			dmabuf->ready = 0;
-			dmabuf->swptr = dmabuf->hwptr = 0;
-			dmabuf->count = dmabuf->total_bytes = 0;
 		}
+		synchronize_irq();
+		dmabuf->ready = 0;
+		dmabuf->swptr = dmabuf->hwptr = 0;
+		dmabuf->count = dmabuf->total_bytes = 0;
 		return 0;
 
 	case SNDCTL_DSP_SYNC:
-		if (file->f_mode & FMODE_WRITE)
-			return drain_dac(state, file->f_flags & O_NONBLOCK);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SYNC\n");
+#endif
+		if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK)
+			return 0;
+		drain_dac(state, 0);
+		stop_dac(state);
+		synchronize_irq();
+		dmabuf->ready = 0;
+		dmabuf->swptr = dmabuf->hwptr = 0;
+		dmabuf->count = dmabuf->total_bytes = 0;
 		return 0;
 
 	case SNDCTL_DSP_SPEED: /* set smaple rate */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SPEED\n");
+#endif
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		if (val >= 0) {
@@ -1368,71 +1423,68 @@
 		return put_user(dmabuf->rate, (int *)arg);
 
 	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_STEREO\n");
+#endif
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-		if(val==0)
-			return -EINVAL;
-		if (file->f_mode & FMODE_WRITE) {
+		if(val==0) {
+			ret = -EINVAL;
+		} else {
+			ret = 1;
+		}
+		if (dmabuf->enable & DAC_RUNNING) {
 			stop_dac(state);
-			dmabuf->ready = 0;
-			dmabuf->fmt |= I810_FMT_STEREO;
 		}
-		if (file->f_mode & FMODE_READ) {
+		if (dmabuf->enable & ADC_RUNNING) {
 			stop_adc(state);
-			dmabuf->ready = 0;
-			dmabuf->fmt |= I810_FMT_STEREO;
 		}
-		return 0;
+		return ret;
 
 	case SNDCTL_DSP_GETBLKSIZE:
 		if (file->f_mode & FMODE_WRITE) {
 			if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
 				return val;
-			return put_user(dmabuf->fragsize, (int *)arg);
 		}
 		if (file->f_mode & FMODE_READ) {
 			if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
 				return val;
-			return put_user(dmabuf->fragsize, (int *)arg);
 		}
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->ossfragsize);
+#endif
+		return put_user(dmabuf->ossfragsize, (int *)arg);
 
 	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETFMTS\n");
+#endif
 		return put_user(AFMT_S16_LE, (int *)arg);
 
 	case SNDCTL_DSP_SETFMT: /* Select sample format */
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (val != AFMT_QUERY) {
-			if (file->f_mode & FMODE_WRITE) {
-				stop_dac(state);
-				dmabuf->ready = 0;
-				dmabuf->fmt |= I810_FMT_16BIT;
-			}
-			if (file->f_mode & FMODE_READ) {
-				stop_adc(state);
-				dmabuf->ready = 0;
-				dmabuf->fmt |= I810_FMT_16BIT;
-			}
-		}
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETFMT\n");
+#endif
 		return put_user(AFMT_S16_LE, (int *)arg);
 
 	case SNDCTL_DSP_CHANNELS:
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (val != 0) {
-			if (file->f_mode & FMODE_WRITE) {
-				stop_dac(state);
-				dmabuf->ready = 0;
-			}
-			if (file->f_mode & FMODE_READ) {
-				stop_adc(state);
-				dmabuf->ready = 0;
-			}
-		}
+#ifdef DEBUG
+		printk("SNDCTL_DSP_CHANNELS\n");
+#endif
 		return put_user(2, (int *)arg);
 
-	case SNDCTL_DSP_POST:
-		/* FIXME: the same as RESET ?? */
+	case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */
+		/* we update the swptr to the end of the last sg segment then return */
+#ifdef DEBUG
+		printk("SNDCTL_DSP_POST\n");
+#endif
+		if(!dmabuf->ready || (dmabuf->enable != DAC_RUNNING))
+			return 0;
+		if((dmabuf->swptr % dmabuf->fragsize) != 0) {
+			val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize);
+			dmabuf->swptr += val;
+			dmabuf->count += val;
+		}
 		return 0;
 
 	case SNDCTL_DSP_SUBDIVIDE:
@@ -1442,6 +1494,9 @@
 			return -EFAULT;
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);
+#endif
 		dmabuf->subdivision = val;
 		dmabuf->ready = 0;
 		return 0;
@@ -1450,15 +1505,27 @@
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 
-		dmabuf->ossfragshift = val & 0xffff;
+		dmabuf->ossfragsize = 1<<(val & 0xffff);
 		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
-		if (dmabuf->ossfragshift < 4)
-			dmabuf->ossfragshift = 4;
-		if (dmabuf->ossfragshift > 15)
-			dmabuf->ossfragshift = 15;
-		if (dmabuf->ossmaxfrags < 4)
+		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;
+		val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
+		if (val < 16384)
+			val = 16384;
+		if (val > 65536)
+			val = 65536;
+		dmabuf->ossfragsize = val/dmabuf->ossmaxfrags;
 		dmabuf->ready = 0;
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
+			dmabuf->ossfragsize, dmabuf->ossmaxfrags);
+#endif
 
 		return 0;
 
@@ -1469,13 +1536,42 @@
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
 		i810_update_ptr(state);
-		abinfo.fragsize = dmabuf->fragsize;
-		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
-		abinfo.fragstotal = dmabuf->numfrag;
-		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		abinfo.fragsize = dmabuf->ossfragsize;
+		abinfo.fragstotal = dmabuf->ossmaxfrags;
+		if(dmabuf->mapped)
+			abinfo.bytes = dmabuf->count;
+		else
+			abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+		abinfo.fragments = abinfo.bytes / dmabuf->ossfragsize;
 		spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes,
+			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
+#endif
 		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
 
+	case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		i810_update_ptr(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.ptr = dmabuf->hwptr;
+		cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->ossfragsize;
+		if (dmabuf->mapped) {
+			dmabuf->count = (dmabuf->dmasize - 
+					 (dmabuf->count & (dmabuf->ossfragsize-1)));
+			__i810_update_lvi(state, 0);
+		}
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
+			cinfo.blocks, cinfo.ptr, dmabuf->count);
+#endif
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
@@ -1483,87 +1579,111 @@
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
 		i810_update_ptr(state);
-		abinfo.fragsize = dmabuf->fragsize;
-		abinfo.bytes = dmabuf->count;
-		abinfo.fragstotal = dmabuf->numfrag;
-		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		abinfo.fragsize = dmabuf->ossfragsize;
+		abinfo.fragstotal = dmabuf->ossmaxfrags;
+		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+		abinfo.fragments = abinfo.bytes / dmabuf->ossfragsize;
 		spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes,
+			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
+#endif
 		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
 
+	case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&state->card->lock, flags);
+		i810_update_ptr(state);
+		cinfo.bytes = dmabuf->total_bytes;
+		cinfo.blocks = dmabuf->count/dmabuf->ossfragsize;
+		cinfo.ptr = dmabuf->hwptr;
+		if (dmabuf->mapped) {
+			dmabuf->count &= dmabuf->ossfragsize-1;
+			__i810_update_lvi(state, 1);
+		}
+		spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
+			cinfo.blocks, cinfo.ptr, dmabuf->count);
+#endif
+		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
 	case SNDCTL_DSP_NONBLOCK:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_NONBLOCK\n");
+#endif
 		file->f_flags |= O_NONBLOCK;
 		return 0;
 
 	case SNDCTL_DSP_GETCAPS:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETCAPS\n");
+#endif
 	    return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
 			    (int *)arg);
 
 	case SNDCTL_DSP_GETTRIGGER:
 		val = 0;
-		if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING)
-			val |= PCM_ENABLE_INPUT;
-		if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING)
-			val |= PCM_ENABLE_OUTPUT;
-		return put_user(val, (int *)arg);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger);
+#endif
+		return put_user(dmabuf->trigger, (int *)arg);
 
 	case SNDCTL_DSP_SETTRIGGER:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-		if (file->f_mode & FMODE_READ && val & PCM_ENABLE_INPUT) {
-			if (dmabuf->enable & DAC_RUNNING)
-				return -ENODEV;
-			if (!dmabuf->read_channel) {
-				dmabuf->ready = 0;
-				dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
-				if (!dmabuf->read_channel)
-					return -ENODEV;
-			}
-			if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
-				return ret;
-			start_adc(state);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
+#endif
+		if( !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
+			stop_adc(state);
 		}
-		if (file->f_mode & FMODE_WRITE && val & PCM_ENABLE_OUTPUT) {
-			if (dmabuf->enable & ADC_RUNNING)
-				return -ENODEV;
+		if( !(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) {
+			stop_dac(state);
+		}
+		dmabuf->trigger = val;
+		if(val & PCM_ENABLE_OUTPUT) {
 			if (!dmabuf->write_channel) {
 				dmabuf->ready = 0;
 				dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
 				if (!dmabuf->write_channel)
-					return -ENODEV;
+					return -EBUSY;
 			}
 			if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 				return ret;
-			start_dac(state);
+			if (dmabuf->mapped) {
+				dmabuf->count = dmabuf->dmasize;
+				i810_update_lvi(state,0);
+			}
+			if (!dmabuf->enable && dmabuf->count > dmabuf->fragsize)
+				start_dac(state);
+		}
+		if(val & PCM_ENABLE_INPUT) {
+			if (!dmabuf->read_channel) {
+				dmabuf->ready = 0;
+				dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
+				if (!dmabuf->read_channel)
+					return -EBUSY;
+			}
+			if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+				return ret;
+			if (dmabuf->mapped) {
+				dmabuf->count = 0;
+				i810_update_lvi(state,1);
+			}
+			if (!dmabuf->enable && dmabuf->count <
+			    (dmabuf->dmasize - dmabuf->fragsize))
+				start_adc(state);
 		}
 		return 0;
 
-	case SNDCTL_DSP_GETIPTR:
-		if (!(file->f_mode & FMODE_READ))
-			return -EINVAL;
-		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
-		cinfo.bytes = dmabuf->total_bytes;
-		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
-		cinfo.ptr = dmabuf->hwptr;
-		if (dmabuf->mapped)
-			dmabuf->count &= dmabuf->fragsize-1;
-		spin_unlock_irqrestore(&state->card->lock, flags);
-		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
-
-	case SNDCTL_DSP_GETOPTR:
-		if (!(file->f_mode & FMODE_WRITE))
-			return -EINVAL;
-		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
-		cinfo.bytes = dmabuf->total_bytes;
-		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
-		cinfo.ptr = dmabuf->hwptr;
-		if (dmabuf->mapped)
-			dmabuf->count &= dmabuf->fragsize-1;
-		spin_unlock_irqrestore(&state->card->lock, flags);
-		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
-
 	case SNDCTL_DSP_SETDUPLEX:
+#ifdef DEBUG
+		printk("SNDCTL_DSP_SETDUPLEX\n");
+#endif
 		return -EINVAL;
 
 	case SNDCTL_DSP_GETODELAY:
@@ -1573,16 +1693,27 @@
 		i810_update_ptr(state);
 		val = dmabuf->count;
 		spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG
+		printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count);
+#endif
 		return put_user(val, (int *)arg);
 
 	case SOUND_PCM_READ_RATE:
+#ifdef DEBUG
+		printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate);
+#endif
 		return put_user(dmabuf->rate, (int *)arg);
 
 	case SOUND_PCM_READ_CHANNELS:
-		return put_user((dmabuf->fmt & I810_FMT_STEREO) ? 2 : 1,
-				(int *)arg);
+#ifdef DEBUG
+		printk("SOUND_PCM_READ_CHANNELS\n");
+#endif
+		return put_user(2, (int *)arg);
 
 	case SOUND_PCM_READ_BITS:
+#ifdef DEBUG
+		printk("SOUND_PCM_READ_BITS\n");
+#endif
 		return put_user(AFMT_S16_LE, (int *)arg);
 
 	case SNDCTL_DSP_MAPINBUF:
@@ -1590,6 +1721,9 @@
 	case SNDCTL_DSP_SETSYNCRO:
 	case SOUND_PCM_WRITE_FILTER:
 	case SOUND_PCM_READ_FILTER:
+#ifdef DEBUG
+		printk("SNDCTL_* -EINVAL\n");
+#endif
 		return -EINVAL;
 	}
 	return -EINVAL;
@@ -1621,7 +1755,7 @@
 	if (!state)
 		return -ENODEV;
 
- found_virt:
+found_virt:
 	/* initialize the virtual channel */
 	state->virt = i;
 	state->card = card;
@@ -1629,33 +1763,36 @@
 	init_waitqueue_head(&dmabuf->wait);
 	init_MUTEX(&state->open_sem);
 	file->private_data = state;
+	dmabuf->trigger = 0;
 
 	/* allocate hardware channels */
 	if(file->f_mode & FMODE_READ) {
 		if((dmabuf->read_channel = card->alloc_rec_pcm_channel(card)) == NULL) {
 			kfree (card->states[i]);
 			card->states[i] = NULL;;
-			return -ENODEV;
+			return -EBUSY;
 		}
-		i810_set_adc_rate(state, 48000);
+		i810_set_adc_rate(state, 8000);
+		dmabuf->trigger |= PCM_ENABLE_INPUT;
 	}
 	if(file->f_mode & FMODE_WRITE) {
 		if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) {
 			kfree (card->states[i]);
 			card->states[i] = NULL;;
-			return -ENODEV;
+			return -EBUSY;
 		}
-		i810_set_dac_rate(state, 48000);
+		i810_set_dac_rate(state, 8000);
+		dmabuf->trigger |= PCM_ENABLE_OUTPUT;
 	}
 		
 	down(&state->open_sem);
 
 	/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
 	   should be default to unsigned 8-bits, mono, with sample rate 8kHz and
-	   /dev/dspW will accept 16-bits sample */
-	dmabuf->fmt &= ~I810_FMT_MASK;
-	dmabuf->fmt |= I810_FMT_16BIT;
-	dmabuf->ossfragshift = 0;
+	   /dev/dspW will accept 16-bits sample, but we don't support those so we
+	   set it immediately to stereo and 16bit, which is all we do support */
+	dmabuf->fmt |= I810_FMT_16BIT | I810_FMT_STEREO;
+	dmabuf->ossfragsize = 0;
 	dmabuf->ossmaxfrags  = 0;
 	dmabuf->subdivision  = 0;
 
@@ -1671,22 +1808,19 @@
 	struct dmabuf *dmabuf = &state->dmabuf;
 
 	lock_kernel();
-	if (file->f_mode & FMODE_WRITE) {
-	}
 
-	/* stop DMA state machine and free DMA buffers/channels */
 	down(&state->open_sem);
 
-	if (dmabuf->enable & DAC_RUNNING) {
-		i810_clear_tail(state);
-		drain_dac(state, file->f_flags & O_NONBLOCK);
+	/* 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);
 		stop_dac(state);
-		dealloc_dmabuf(state);
 	}
 	if(dmabuf->enable & ADC_RUNNING) {
 		stop_adc(state);
-		dealloc_dmabuf(state);
 	}
+	dealloc_dmabuf(state);
 	if (file->f_mode & FMODE_WRITE) {
 		state->card->free_pcm_channel(state->card, dmabuf->write_channel->num);
 	}
@@ -1827,6 +1961,19 @@
 		if (ac97_probe_codec(codec) == 0)
 			break;
 
+		/* power up everything, modify this when implementing power saving */
+		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--)
+		{
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(HZ/20);
+		}
+
+		/* Don't attempt to get eid until powerup is complete */
 		eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
 		
 		if(eid==0xFFFFFF)
@@ -1845,18 +1992,6 @@
 			printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
 		else
 		{
-			/* power up everything, modify this when implementing power saving */
-			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--)
-			{
-				current->state = TASK_UNINTERRUPTIBLE;
-				schedule_timeout(HZ/20);
-			}
-
 			/* Enable variable rate mode */
 			i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
 			i810_ac97_set(codec,AC97_EXTENDED_STATUS,
@@ -1996,6 +2131,7 @@
 MODULE_DESCRIPTION("Intel 810 audio support");
 MODULE_PARM(ftsodell, "i");
 MODULE_PARM(clocking, "i");
+MODULE_PARM(strict_clocking, "i");
 
 #define I810_MODULE_NAME "intel810_audio"
 
@@ -2036,6 +2172,7 @@
 		init_waitqueue_head(&dmabuf->wait);
 		init_MUTEX(&state->open_sem);
 		dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
+		dmabuf->trigger = PCM_ENABLE_OUTPUT;
 		i810_set_dac_rate(state, 48000);
 		if(prog_dmabuf(state, 0) != 0) {
 			goto config_out_nodmabuf;
@@ -2044,25 +2181,30 @@
 			goto config_out;
 		}
 		dmabuf->count = dmabuf->dmasize;
+		outb(31,card->iobase+dmabuf->write_channel->port+OFF_LVI);
 		save_flags(flags);
 		cli();
 		start_dac(state);
-		offset = i810_get_dma_addr(state);
+		offset = i810_get_dma_addr(state, 0);
 		mdelay(50);
-		new_offset = i810_get_dma_addr(state);
+		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
 		printk("i810_audio: %d bytes in 50 milliseconds\n", i);
+#endif
+		if(i == 0)
+			goto config_out;
 		i = i / 4 * 20;
 		if (i > 48500 || i < 47500) {
 			clocking = clocking * clocking / i;
-			printk("i810_audio: setting clocking to %d to compensate\n", clocking);
+			printk("i810_audio: setting clocking to %d\n", clocking);
 		}
-config_out_nodmabuf:
-		dealloc_dmabuf(state);
 config_out:
+		dealloc_dmabuf(state);
+config_out_nodmabuf:
 		state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num);
 		kfree(state);
 		card->states[0] = NULL;

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