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

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

diff -u --recursive --new-file v2.4.2/linux/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
@@ -194,8 +194,12 @@
 /* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
 #define NR_AC97		2
 
+/* 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 */
 static const unsigned sample_size[] = { 1, 2, 2, 4 };
-static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */
+/* values are one less than might be expected */
+static const unsigned sample_shift[] = { -1, 0, 0, 1 };
 
 enum {
 	ICH82801AA = 0,
@@ -246,7 +250,8 @@
 		unsigned char fmt, enable;
 
 		/* hardware channel */
-		struct i810_channel *channel;
+		struct i810_channel *read_channel;
+		struct i810_channel *write_channel;
 
 		/* OSS buffer management stuff */
 		void *rawbuf;
@@ -258,7 +263,7 @@
 		/* our buffer acts like a circular ring */
 		unsigned hwptr;		/* where dma last started, updated by update_ptr */
 		unsigned swptr;		/* where driver last clear/filled, updated by read/write */
-		int count;		/* bytes to be comsumed or been generated by dma machine */
+		int count;		/* bytes to be consumed or been generated by dma machine */
 		unsigned total_bytes;	/* total bytes dmaed by hardware */
 
 		unsigned error;		/* number of over/underruns */
@@ -313,6 +318,7 @@
 	/* Function support */
 	struct i810_channel *(*alloc_pcm_channel)(struct i810_card *);
 	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);
 };
 
@@ -356,9 +362,6 @@
 	if(card->channel[1].used==1)
 		return NULL;
 	card->channel[1].used=1;
-	card->channel[1].offset = 0;
-	card->channel[1].port = 0x10;
-	card->channel[1].num=1;
 	return &card->channel[1];
 }
 
@@ -367,12 +370,17 @@
 	if(card->channel[0].used==1)
 		return NULL;
 	card->channel[0].used=1;
-	card->channel[0].offset = 0;
-	card->channel[0].port = 0x00;
-	card->channel[1].num=0;
 	return &card->channel[0];
 }
 
+static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card)
+{
+	if(card->channel[2].used==1)
+		return NULL;
+	card->channel[2].used=1;
+	return &card->channel[2];
+}
+
 static void i810_free_pcm_channel(struct i810_card *card, int channel)
 {
 	card->channel[channel].used=0;
@@ -382,7 +390,7 @@
 static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate)
 {	
 	struct dmabuf *dmabuf = &state->dmabuf;
-	u32 dacp;
+	u32 dacp, new_rate;
 	struct ac97_codec *codec=state->card->ac97_codec[0];
 	
 	if(!(state->card->ac97_features&0x0001))
@@ -395,6 +403,7 @@
 		rate = 48000;
 	if (rate < 8000)
 		rate = 8000;
+	dmabuf->rate = rate;
 		
 	/*
 	 *	Adjust for misclocked crap
@@ -402,12 +411,6 @@
 	 
 	rate = ( rate * clocking)/48000;
 	
-	/* Analog codecs can go lower via magic registers but others
-	   might not */
-	   
-	if(rate < 8000)
-		rate = 8000;
-
 	if(rate != i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE))
 	{
 		/* Power down the DAC */
@@ -415,24 +418,25 @@
 		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0200);
 		/* Load the rate and read the effective rate */
 		i810_ac97_set(codec, AC97_PCM_FRONT_DAC_RATE, rate);
-		rate=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE);
+		new_rate=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE);
 		/* Power it back up */
 		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);
+		if(new_rate != rate) {
+			dmabuf->rate = (new_rate * 48000)/clocking;
+			rate = new_rate;
+		}
 	}
-	rate=(rate * 48000) / clocking;
-	dmabuf->rate = rate;
 #ifdef DEBUG
-	printk("i810_audio: called i810_set_dac_rate : rate = %d\n", rate);
+	printk("i810_audio: called i810_set_dac_rate : rate = %d/%d\n", dmabuf->rate, rate);
 #endif
-
-	return rate;
+	return dmabuf->rate;
 }
 
 /* set recording sample rate */
 static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	u32 dacp;
+	u32 dacp, new_rate;
 	struct ac97_codec *codec=state->card->ac97_codec[0];
 	
 	if(!(state->card->ac97_features&0x0001))
@@ -445,6 +449,7 @@
 		rate = 48000;
 	if (rate < 8000)
 		rate = 8000;
+	dmabuf->rate = rate;
 
 	/*
 	 *	Adjust for misclocked crap
@@ -452,12 +457,6 @@
 	 
 	rate = ( rate * clocking)/48000;
 	
-	/* Analog codecs can go lower via magic registers but others
-	   might not */
-	   
-	if(rate < 8000)
-		rate = 8000;
-
 	if(rate != i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE))
 	{
 		/* Power down the ADC */
@@ -465,16 +464,18 @@
 		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0100);
 		/* Load the rate and read the effective rate */
 		i810_ac97_set(codec, AC97_PCM_LR_DAC_RATE, rate);
-		rate=i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE);
+		new_rate=i810_ac97_get(codec, AC97_PCM_LR_DAC_RATE);
 		/* Power it back up */
 		i810_ac97_set(codec, AC97_POWER_CONTROL, dacp);
+		if(new_rate != rate) {
+			dmabuf->rate = (new_rate * 48000)/clocking;
+			rate = new_rate;
+		}
 	}
-	rate = (rate * 48000) / clocking;
-	dmabuf->rate = rate;
 #ifdef DEBUG
-	printk("i810_audio: called i810_set_adc_rate : rate = %d\n", rate);
+	printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);
 #endif
-	return rate;
+	return dmabuf->rate;
 }
 
 /* prepare channel attributes for playback */ 
@@ -508,10 +509,18 @@
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned int civ, offset;
-	struct i810_channel *c = dmabuf->channel;
+	struct i810_channel *c;
 	
 	if (!dmabuf->enable)
 		return 0;
+	if (dmabuf->enable & DAC_RUNNING)
+		c = dmabuf->write_channel;
+	else if (dmabuf->enable & ADC_RUNNING)
+		c = dmabuf->read_channel;
+	else {
+		printk("i810_audio: invalid dmabuf->enable state in get_dma_addr\n");
+		return 0;
+	}
 	do {
 		civ = inb(state->card->iobase+c->port+OFF_CIV);
 		offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -
@@ -523,12 +532,17 @@
 	return offset;
 }
 
-static void resync_dma_ptrs(struct i810_state *state)
+static void resync_dma_ptrs(struct i810_state *state, int rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	struct i810_channel *c = dmabuf->channel;
+	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);
 	
@@ -597,23 +611,20 @@
 
 	spin_lock_irqsave(&card->lock, flags);
 	if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) {
-		if(!(dmabuf->enable&DAC_RUNNING))
-		{
-			dmabuf->enable |= DAC_RUNNING;
-			outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR);
-		}
+		dmabuf->enable |= DAC_RUNNING;
+		outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR);
 	}
 	spin_unlock_irqrestore(&card->lock, flags);
 }
 
-#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
+#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
 #define DMABUF_MINORDER 1
 
 /* allocate DMA buffer, playback and recording buffer should be allocated seperately */
 static int alloc_dmabuf(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	void *rawbuf;
+	void *rawbuf= NULL;
 	int order;
 	struct page *page, *pend;
 
@@ -664,6 +675,7 @@
 static int prog_dmabuf(struct i810_state *state, unsigned rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
+	struct i810_channel *c;
 	struct sg_item *sg;
 	unsigned bytepersec;
 	unsigned bufsize;
@@ -673,7 +685,7 @@
 	int i;
 
 	spin_lock_irqsave(&state->card->lock, flags);
-	resync_dma_ptrs(state);
+	resync_dma_ptrs(state, rec);
 	dmabuf->total_bytes = 0;
 	dmabuf->count = dmabuf->error = 0;
 	spin_unlock_irqrestore(&state->card->lock, flags);
@@ -684,7 +696,8 @@
 			return ret;
 
 	/* FIXME: figure out all this OSS fragment stuff */
-	bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+	/* 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)
@@ -709,44 +722,52 @@
 	memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80,
 	       dmabuf->dmasize);
 
-	/*
-	 *	Now set up the ring 
-	 */
-
-	sg=&dmabuf->channel->sg[0];
 	fragsize = bufsize / SG_LEN;
-	
 	/*
-	 *	Load up 32 sg entries and take an interrupt at half
-	 *	way (we might want more interrupts later..) 
+	 *	Now set up the ring 
 	 */
+	if(dmabuf->read_channel)
+		c = dmabuf->read_channel;
+	else
+		c = dmabuf->write_channel;
+	while(c != NULL) {
+		sg=&c->sg[0];
+		/*
+		 *	Load up 32 sg entries and take an interrupt at half
+		 *	way (we might want more interrupts later..) 
+		 */
 	  
-	for(i=0;i<32;i++)
-	{
-		sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i);
-		sg->control=(fragsize>>1);
-		sg->control|=CON_IOC;
-		sg++;
-	}
+		for(i=0;i<32;i++)
+		{
+			sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i);
+			sg->control=(fragsize>>sample_shift[dmabuf->fmt]);
+			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);
 
-	spin_lock_irqsave(&state->card->lock, flags);
-	outb(2, state->card->iobase+dmabuf->channel->port+OFF_CR);   /* reset DMA machine */
-	outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR);
-	outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI);
-	outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV);
+		if (c == dmabuf->read_channel) {
+			i810_rec_setup(state);
+		} else {
+			i810_play_setup(state);
+		}
+		spin_unlock_irqrestore(&state->card->lock, flags);
 
-	if (rec) {
-		i810_rec_setup(state);
-	} else {
-		i810_play_setup(state);
+		if(c != dmabuf->write_channel)
+			c = dmabuf->write_channel;
+		else
+			c = NULL;
 	}
-	spin_unlock_irqrestore(&state->card->lock, flags);
-
+	
 	/* set the ready flag for the dma buffer */
 	dmabuf->ready = 1;
 
 #ifdef DEBUG
-	printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, "
+	printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, "
 	       "fragsize = %d dmasize = %d\n",
 	       dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
 	       dmabuf->fragsize, dmabuf->dmasize);
@@ -912,7 +933,6 @@
 {
 	int i;
 
-//	printk("CHANNEL IRQ .. ");	
 	for(i=0;i<NR_HW_CH;i++)
 	{
 		struct i810_state *state = card->states[i];
@@ -924,38 +944,35 @@
 			continue;
 		if(!state->dmabuf.ready)
 			continue;
-		c=state->dmabuf.channel;
+		if(state->dmabuf.enable & DAC_RUNNING)
+			c=state->dmabuf.write_channel;
+		else
+			c=state->dmabuf.read_channel;
 		
 		port+=c->port;
 		
-//		printk("PORT %lX (", port);
-		
 		status = inw(port + OFF_SR);
 		
-//		printk("ST%d ", status);
-		
-		if(status & DMA_INT_LVI)
-		{
-			/* Back to the start */
-//			printk("LVI - STOP");
-			outb((inb(port+OFF_CIV)-1)&31, port+OFF_LVI);
-			i810_update_ptr(state);
-			outb(0, port + OFF_CR);
-		}
 		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);
-//			printk("COMP%d ",x);
 		}
-//		printk(")");
+		if(status & DMA_INT_LVI)
+		{
+			/* Back to the start */
+			i810_update_ptr(state);
+			outb(0, port + OFF_CR);
+		}
 		outw(status & DMA_INT_MASK, port + OFF_SR);
 	}
-//	printk("\n");
 }
 
+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;
@@ -964,13 +981,13 @@
 	spin_lock(&card->lock);
 
 	status = inl(card->iobase + GLOB_STA);
+
 	if(!(status & INT_MASK)) 
 	{
 		spin_unlock(&card->lock);
 		return;  /* not for us */
 	}
 
-//	printk("Interrupt %X: ", status);
 	if(status & (INT_PO|INT_PI|INT_MC))
 		i810_channel_interrupt(card);
 
@@ -1003,6 +1020,15 @@
 		return -ESPIPE;
 	if (dmabuf->mapped)
 		return -ENXIO;
+	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;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
@@ -1100,6 +1126,14 @@
 		return -ESPIPE;
 	if (dmabuf->mapped)
 		return -ENXIO;
+	if (dmabuf->enable & ADC_RUNNING)
+		return -ENODEV;
+	if (!dmabuf->write_channel) {
+		dmabuf->ready = 0;
+		dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+		if(!dmabuf->write_channel)
+			return -ENODEV;
+	}
 	if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
@@ -1132,8 +1166,8 @@
 				return ret;
 			}
 			/* Not strictly correct but works */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
+			tmo = dmabuf->rate << (sample_shift[dmabuf->fmt] + 1);
+			tmo = dmabuf->dmasize * HZ / tmo;
 			/* 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
@@ -1187,22 +1221,27 @@
 	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);
-	}
-	if (file->f_mode & FMODE_READ) {
+	} 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);
 	}
 	spin_lock_irqsave(&state->card->lock, flags);
 	i810_update_ptr(state);
-	if (file->f_mode & FMODE_READ) {
+	if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) {
 		if (dmabuf->count >= (signed)dmabuf->fragsize)
 			mask |= POLLIN | POLLRDNORM;
 	}
-	if (file->f_mode & FMODE_WRITE) {
+	if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING) {
 		if (dmabuf->mapped) {
 			if (dmabuf->count >= (signed)dmabuf->fragsize)
 				mask |= POLLOUT | POLLWRNORM;
@@ -1223,11 +1262,19 @@
 	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)
 			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)
 			goto out;
 	} else 
@@ -1245,6 +1292,9 @@
 		goto out;
 	dmabuf->mapped = 1;
 	ret = 0;
+#ifdef DEBUG
+	printk("i810_audio: mmap'ed %d bytes of data space\n", size);
+#endif
 out:
 	unlock_kernel();
 	return ret;
@@ -1277,14 +1327,14 @@
 			stop_dac(state);
 			synchronize_irq();
 			dmabuf->ready = 0;
-			resync_dma_ptrs(state);
+			resync_dma_ptrs(state, 0);
 			dmabuf->swptr = dmabuf->hwptr = 0;
 			dmabuf->count = dmabuf->total_bytes = 0;
 		}
 		if (file->f_mode & FMODE_READ) {
 			stop_adc(state);
 			synchronize_irq();
-			resync_dma_ptrs(state);
+			resync_dma_ptrs(state, 1);
 			dmabuf->ready = 0;
 			dmabuf->swptr = dmabuf->hwptr = 0;
 			dmabuf->count = dmabuf->total_bytes = 0;
@@ -1325,23 +1375,23 @@
 		if (file->f_mode & FMODE_WRITE) {
 			stop_dac(state);
 			dmabuf->ready = 0;
-			dmabuf->fmt = I810_FMT_STEREO;
+			dmabuf->fmt |= I810_FMT_STEREO;
 		}
 		if (file->f_mode & FMODE_READ) {
 			stop_adc(state);
 			dmabuf->ready = 0;
-			dmabuf->fmt = I810_FMT_STEREO;
+			dmabuf->fmt |= I810_FMT_STEREO;
 		}
 		return 0;
 
 	case SNDCTL_DSP_GETBLKSIZE:
 		if (file->f_mode & FMODE_WRITE) {
-			if ((val = prog_dmabuf(state, 0)))
+			if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
 				return val;
 			return put_user(dmabuf->fragsize, (int *)arg);
 		}
 		if (file->f_mode & FMODE_READ) {
-			if ((val = prog_dmabuf(state, 1)))
+			if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
 				return val;
 			return put_user(dmabuf->fragsize, (int *)arg);
 		}
@@ -1356,10 +1406,12 @@
 			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;
 			}
 		}
 		return put_user(AFMT_S16_LE, (int *)arg);
@@ -1391,6 +1443,7 @@
 		if (val != 1 && val != 2 && val != 4)
 			return -EINVAL;
 		dmabuf->subdivision = val;
+		dmabuf->ready = 0;
 		return 0;
 
 	case SNDCTL_DSP_SETFRAGMENT:
@@ -1405,13 +1458,14 @@
 			dmabuf->ossfragshift = 15;
 		if (dmabuf->ossmaxfrags < 4)
 			dmabuf->ossmaxfrags = 4;
+		dmabuf->ready = 0;
 
 		return 0;
 
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
-		if (!dmabuf->enable && (val = prog_dmabuf(state, 0)) != 0)
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
 		i810_update_ptr(state);
@@ -1425,7 +1479,7 @@
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
-		if (!dmabuf->enable && (val = prog_dmabuf(state, 1)) != 0)
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
 		i810_update_ptr(state);
@@ -1446,30 +1500,40 @@
 
 	case SNDCTL_DSP_GETTRIGGER:
 		val = 0;
-		if (file->f_mode & FMODE_READ && dmabuf->enable)
+		if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING)
 			val |= PCM_ENABLE_INPUT;
-		if (file->f_mode & FMODE_WRITE && dmabuf->enable)
+		if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING)
 			val |= PCM_ENABLE_OUTPUT;
 		return put_user(val, (int *)arg);
 
 	case SNDCTL_DSP_SETTRIGGER:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-		if (file->f_mode & FMODE_READ) {
-			if (val & PCM_ENABLE_INPUT) {
-				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
-					return ret;
-				start_adc(state);
-			} else
-				stop_adc(state);
+		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);
 		}
-		if (file->f_mode & FMODE_WRITE) {
-			if (val & PCM_ENABLE_OUTPUT) {
-				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
-					return ret;
-				start_dac(state);
-			} else
-				stop_dac(state);
+		if (file->f_mode & FMODE_WRITE && val & PCM_ENABLE_OUTPUT) {
+			if (dmabuf->enable & ADC_RUNNING)
+				return -ENODEV;
+			if (!dmabuf->write_channel) {
+				dmabuf->ready = 0;
+				dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+				if (!dmabuf->write_channel)
+					return -ENODEV;
+			}
+			if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+				return ret;
+			start_dac(state);
 		}
 		return 0;
 
@@ -1558,18 +1622,6 @@
 		return -ENODEV;
 
  found_virt:
-	/* found a free virtual channel, allocate hardware channels */
-	if(file->f_mode & FMODE_READ)
-		dmabuf->channel = card->alloc_rec_pcm_channel(card);
-	else
-		dmabuf->channel = card->alloc_pcm_channel(card);
-		
-	if (dmabuf->channel == NULL) {
-		kfree (card->states[i]);
-		card->states[i] = NULL;;
-		return -ENODEV;
-	}
-
 	/* initialize the virtual channel */
 	state->virt = i;
 	state->card = card;
@@ -1578,28 +1630,34 @@
 	init_MUTEX(&state->open_sem);
 	file->private_data = state;
 
+	/* 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;
+		}
+		i810_set_adc_rate(state, 48000);
+	}
+	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;
+		}
+		i810_set_dac_rate(state, 48000);
+	}
+		
 	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 */
-	if (file->f_mode & FMODE_WRITE) {
-		dmabuf->fmt &= ~I810_FMT_MASK;
-		dmabuf->fmt |= I810_FMT_16BIT;
-		dmabuf->ossfragshift = 0;
-		dmabuf->ossmaxfrags  = 0;
-		dmabuf->subdivision  = 0;
-		i810_set_dac_rate(state, 48000);
-	}
-
-	if (file->f_mode & FMODE_READ) {
-		dmabuf->fmt &= ~I810_FMT_MASK;
-		dmabuf->fmt |= I810_FMT_16BIT;
-		dmabuf->ossfragshift = 0;
-		dmabuf->ossmaxfrags  = 0;
-		dmabuf->subdivision  = 0;
-		i810_set_adc_rate(state, 48000);
-	}
+	dmabuf->fmt &= ~I810_FMT_MASK;
+	dmabuf->fmt |= I810_FMT_16BIT;
+	dmabuf->ossfragshift = 0;
+	dmabuf->ossmaxfrags  = 0;
+	dmabuf->subdivision  = 0;
 
 	state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
 	up(&state->open_sem);
@@ -1614,29 +1672,33 @@
 
 	lock_kernel();
 	if (file->f_mode & FMODE_WRITE) {
-		i810_clear_tail(state);
-		drain_dac(state, file->f_flags & O_NONBLOCK);
 	}
 
 	/* stop DMA state machine and free DMA buffers/channels */
 	down(&state->open_sem);
 
-	if (file->f_mode & FMODE_WRITE) {
+	if (dmabuf->enable & DAC_RUNNING) {
+		i810_clear_tail(state);
+		drain_dac(state, file->f_flags & O_NONBLOCK);
 		stop_dac(state);
 		dealloc_dmabuf(state);
-		state->card->free_pcm_channel(state->card, dmabuf->channel->num);
 	}
-	if (file->f_mode & FMODE_READ) {
+	if(dmabuf->enable & ADC_RUNNING) {
 		stop_adc(state);
 		dealloc_dmabuf(state);
-		state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		state->card->free_pcm_channel(state->card, dmabuf->write_channel->num);
+	}
+	if (file->f_mode & FMODE_READ) {
+		state->card->free_pcm_channel(state->card, dmabuf->read_channel->num);
 	}
 
 	/* we're covered by the open_sem */
 	up(&state->open_sem);
 	
-	kfree(state->card->states[state->virt]);
 	state->card->states[state->virt] = NULL;
+	kfree(state);
 	unlock_kernel();
 
 	return 0;
@@ -1688,16 +1750,11 @@
 	for (card = devs; card != NULL; card = card->next)
 		for (i = 0; i < NR_AC97; i++)
 			if (card->ac97_codec[i] != NULL &&
-			    card->ac97_codec[i]->dev_mixer == minor)
-				goto match;
-
-	if (!card)
-		return -ENODEV;
-
- match:
-	file->private_data = card->ac97_codec[i];
-
-	return 0;
+			    card->ac97_codec[i]->dev_mixer == minor) {
+				file->private_data = card->ac97_codec[i];
+				return 0;
+			}
+	return -ENODEV;
 }
 
 static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
@@ -1788,10 +1845,6 @@
 			printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
 		else
 		{
-			/* Enable variable rate mode */
-			i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
-			i810_ac97_set(codec,AC97_EXTENDED_STATUS,
-				i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
 			/* power up everything, modify this when implementing power saving */
 			i810_ac97_set(codec, AC97_POWER_CONTROL,
 				i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
@@ -1804,6 +1857,11 @@
 				schedule_timeout(HZ/20);
 			}
 
+			/* Enable variable rate mode */
+			i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
+			i810_ac97_set(codec,AC97_EXTENDED_STATUS,
+				i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
+
 			if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1))
 			{
 				printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n");
@@ -1833,14 +1891,15 @@
 {
 	struct i810_card *card;
 
-	if (!pci_dma_supported(pci_dev, I810_DMA_MASK)) {
+	if (pci_enable_device(pci_dev))
+		return -EIO;
+
+	if (pci_set_dma_mask(pci_dev, I810_DMA_MASK)) {
 		printk(KERN_ERR "intel810: architecture does not support"
 		       " 32bit PCI busmaster DMA\n");
 		return -ENODEV;
 	}
 
-	if (pci_enable_device(pci_dev))
-		return -EIO;
 	if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) {
 		printk(KERN_ERR "i810_audio: out of memory\n");
 		return -ENOMEM;
@@ -1865,7 +1924,17 @@
 
 	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;
 
 	/* claim our iospace and irq */
 	request_region(card->iobase, 64, card_names[pci_id->driver_data]);
@@ -1900,7 +1969,6 @@
 		return -ENODEV;
 	}
 	pci_dev->driver_data = card;
-	pci_dev->dma_mask = I810_DMA_MASK;
 	return 0;
 }
 
@@ -1938,20 +2006,86 @@
 	remove:		i810_remove,
 };
 
+static void __init i810_configure_clocking (void)
+{
+	struct i810_card *card;
+	struct i810_state *state;
+	struct dmabuf *dmabuf;
+	unsigned int i, offset, new_offset;
+	unsigned long flags;
+
+	card = devs;
+	/* We could try to set the clocking for multiple cards, but can you even have
+	 * more than one i810 in a machine?  Besides, clocking is global, so unless
+	 * someone actually thinks more than one i810 in a machine is possible and
+	 * decides to rewrite that little bit, setting the rate for more than one card
+	 * is a waste of time.
+	 */
+	if(card != NULL) {
+		state = card->states[0] = (struct i810_state *)
+					kmalloc(sizeof(struct i810_state), GFP_KERNEL);
+		if (state == NULL)
+			return;
+		memset(state, 0, sizeof(struct i810_state));
+		dmabuf = &state->dmabuf;
+
+		dmabuf->write_channel = card->alloc_pcm_channel(card);
+		state->virt = 0;
+		state->card = card;
+		state->magic = I810_STATE_MAGIC;
+		init_waitqueue_head(&dmabuf->wait);
+		init_MUTEX(&state->open_sem);
+		dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT;
+		i810_set_dac_rate(state, 48000);
+		if(prog_dmabuf(state, 0) != 0) {
+			goto config_out_nodmabuf;
+		}
+		if(dmabuf->dmasize < 16384) {
+			goto config_out;
+		}
+		dmabuf->count = dmabuf->dmasize;
+		save_flags(flags);
+		cli();
+		start_dac(state);
+		offset = i810_get_dma_addr(state);
+		mdelay(50);
+		new_offset = i810_get_dma_addr(state);
+		stop_dac(state);
+		outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR);
+		restore_flags(flags);
+		i = new_offset - offset;
+		printk("i810_audio: %d bytes in 50 milliseconds\n", i);
+		i = i / 4 * 20;
+		if (i > 48500 || i < 47500) {
+			clocking = clocking * clocking / i;
+			printk("i810_audio: setting clocking to %d to compensate\n", clocking);
+		}
+config_out_nodmabuf:
+		dealloc_dmabuf(state);
+config_out:
+		state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num);
+		kfree(state);
+		card->states[0] = NULL;
+	}
+}
+
 static int __init i810_init_module (void)
 {
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
 
-	if(ftsodell==1)
-		clocking=41194;
-		
 	printk(KERN_INFO "Intel 810 + AC97 Audio, version "
 	       DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
 
 	if (!pci_register_driver(&i810_pci_driver)) {
 		pci_unregister_driver(&i810_pci_driver);
                 return -ENODEV;
+	}
+	if(ftsodell != 0) {
+		printk("i810_audio: ftsodell is now a deprecated option.\n");
+	}
+	if(clocking == 48000) {
+		i810_configure_clocking();
 	}
 	return 0;
 }

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