linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
To: torvalds@transmeta.com, alan@lxorguk.ukuu.org.uk,
	linux-kernel@vger.kernel.org
Subject: [PATCH] i810_audio 2.4.0-test11
Date: Fri, 01 Dec 2000 12:18:46 +0100	[thread overview]
Message-ID: <3A278916.6FF0C5DE@fujitsu-siemens.com> (raw)

This patch makes the same changes (dma_prog and poll) that are already made to
some other OSS drivers (test12-pre3). Further it finally includes all modifications
made in 2.2.18 (Alan: why do you do them line by line ?).

It implements mono output and fixes a bug in the dma logic (reset necessary 
because some descriptors are already prefetched and are not updated
when the dma is only halted and not reset). There is still a bug in the
device close handling, it gives an occasional dma overrun error.
 
It has been tested on by 3 people on 4 different machines with 3 different
chip sets and at least 3 different codecs, so it really should work !
And it does the same things the alsa driver does.


diff -u --recursive linux-2.4.0-test11-org/drivers/sound/i810_audio.c linux-2.4.0-test11-clean/drivers/sound/i810_audio.c
--- linux-2.4.0-test11-org/drivers/sound/i810_audio.c	Thu Nov  9 02:09:50 2000
+++ linux-2.4.0-test11-clean/drivers/sound/i810_audio.c	Thu Nov 30 14:22:11 2000
@@ -10,6 +10,13 @@
  *	Extended by: Zach Brown <zab@redhat.com>  
  *			and others..
  *
+ *	Modified to support mono audio out, not supported in mapped mode.
+ *	The chip can do stereo only, the output buffer always holds stereo data.
+ *	I think it now conforms a little bit to the OSS Programmers Guide 1.11
+ *	For me mono and stereo mp3 files and wav files now work with xmms and mpg123
+ *	and other tools. And KDE sounds now work.
+ *	Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
+ *
  *  Hardware Provided By:
  *	Analog Devices (A major AC97 codec maker)
  *	Intel Corp  (you've probably heard of them already)
@@ -107,10 +114,6 @@
 #define ADC_RUNNING	1
 #define DAC_RUNNING	2
 
-#define I810_FMT_16BIT	1
-#define I810_FMT_STEREO	2
-#define I810_FMT_MASK	3
-
 /* the 810's array of pointers to data buffers */
 
 struct sg_item {
@@ -131,15 +134,16 @@
 	struct sg_item sg[SG_LEN];	/* 32*8 */
 	u32 offset;			/* 4 */
 	u32 port;			/* 4 */
-	u32 used;
-	u32 num;
+	u32 used;			/* 4 */
+	u32 num;			/* 4 */
 };
 
 /*
  * we have 3 seperate dma engines.  pcm in, pcm out, and mic.
  * each dma engine has controlling registers.  These goofy
  * names are from the datasheet, but make it easy to write
- * code while leafing through it.
+ * code while leafing through it. Right now we don't support
+ * the MIC input.
  */
 
 #define ENUM_ENGINE(PRE,DIG) 									\
@@ -183,7 +187,8 @@
 #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
 
 
-#define DRIVER_VERSION "0.01"
+/* This version is the based  on version 0.17 in the 2.2.18 kernel */
+#define DRIVER_VERSION "0.18 (for 2.4.0-test11)"
 
 /* magic numbers to protect our data structures */
 #define I810_CARD_MAGIC		0x5072696E /* "Prin" */
@@ -194,9 +199,6 @@
 /* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
 #define NR_AC97		2
 
-static const unsigned sample_size[] = { 1, 2, 2, 4 };
-static const unsigned sample_shift[] = { 0, 1, 1, 2 };
-
 enum {
 	ICH82801AA = 0,
 	ICH82901AB,
@@ -243,7 +245,9 @@
 	struct dmabuf {
 		/* wave sample stuff */
 		unsigned int rate;
-		unsigned char fmt, enable;
+	        unsigned int bps_buffer;	/* bytes per sample in the dma buffer */
+		unsigned int bps_user;		/* bytes per sample for the user */
+		unsigned char enable;
 
 		/* hardware channel */
 		struct i810_channel *channel;
@@ -259,13 +263,12 @@
 		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 */
-		unsigned total_bytes;	/* total bytes dmaed by hardware */
 
 		unsigned error;		/* number of over/underruns */
 		wait_queue_head_t wait;	/* put process on wait queue when no more space in buffer */
 
 		/* redundant, but makes calculations easier */
-		unsigned fragsize;
+		unsigned fragsize;	/* size of a fragment in the dma buffer */
 		unsigned dmasize;
 		unsigned fragsamples;
 
@@ -277,6 +280,10 @@
 		unsigned ossfragshift;
 		int ossmaxfrags;
 		unsigned subdivision;
+
+		/* These fields are used by SNDCTL_DSP_GETIPTR and GETOPTR only */
+		unsigned total_bytes;	/* total bytes dmaed by hardware */
+		unsigned blocks;	/* fragments dmaed by hardware */
 	} dmabuf;
 };
 
@@ -319,11 +326,12 @@
 static struct i810_card *devs = NULL;
 
 static int i810_open_mixdev(struct inode *inode, struct file *file);
+static int i810_release_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 loff_t i810_llseek(struct file *file, loff_t offset, int origin);
 
-extern __inline__ unsigned ld2(unsigned int x)
+static __inline__ unsigned ld2(unsigned int x)
 {
 	unsigned r = 0;
 	
@@ -369,7 +377,7 @@
 	card->channel[0].used=1;
 	card->channel[0].offset = 0;
 	card->channel[0].port = 0x00;
-	card->channel[1].num=0;
+	card->channel[0].num=0;
 	return &card->channel[0];
 }
 
@@ -475,53 +483,27 @@
 	printk("i810_audio: called i810_set_adc_rate : rate = %d\n", rate);
 #endif
 	return 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)
+static __inline__ unsigned i810_get_dma_addr(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	u32 offset;
+	unsigned int civ, offset;
 	struct i810_channel *c = dmabuf->channel;
 	
 	if (!dmabuf->enable)
 		return 0;
-	offset = inb(state->card->iobase+c->port+OFF_CIV);
-	offset++;
-	offset&=31;
-	/* Offset has to compensate for the fact we finished the segment
-	   on the IRQ so we are at next_segment,0 */
-//	printk("BANK%d ", offset);
-	offset *= (dmabuf->dmasize/SG_LEN);
-//	printk("DMASZ=%d", dmabuf->dmasize);
-//	offset += 1024-(4*inw(state->card->iobase+c->port+OFF_PICB));
-//	printk("OFF%d ", offset);
+	do {
+		civ = inb(state->card->iobase+c->port+OFF_CIV);
+		offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -
+				2 * inw(state->card->iobase+c->port+OFF_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;
 }
 
@@ -534,11 +516,11 @@
 	offset = inb(state->card->iobase+c->port+OFF_CIV);
 	offset *= (dmabuf->dmasize/SG_LEN);
 	
-	dmabuf->hwptr=dmabuf->swptr = offset;
+	dmabuf->hwptr = dmabuf->swptr = offset;
 }
 	
 /* Stop recording (lock held) */
-extern __inline__ void __stop_adc(struct i810_state *state)
+static __inline__ void __stop_adc(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	struct i810_card *card = state->card;
@@ -572,7 +554,7 @@
 }
 
 /* stop playback (lock held) */
-extern __inline__ void __stop_dac(struct i810_state *state)
+static __inline__ void __stop_dac(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
 	struct i810_card *card = state->card;
@@ -666,6 +648,7 @@
 static int prog_dmabuf(struct i810_state *state, unsigned rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
+	struct i810_card *card = state->card;
 	struct sg_item *sg;
 	unsigned bytepersec;
 	unsigned bufsize;
@@ -674,11 +657,7 @@
 	unsigned fragsize;
 	int i;
 
-	spin_lock_irqsave(&state->card->lock, flags);
-	resync_dma_ptrs(state);
-	dmabuf->total_bytes = 0;
-	dmabuf->count = dmabuf->error = 0;
-	spin_unlock_irqrestore(&state->card->lock, flags);
+	outb(0, card->iobase+dmabuf->channel->port + OFF_CR);	/* halt DMA machine, should have been done already */
 
 	/* allocate DMA buffer if not allocated yet */
 	if (!dmabuf->rawbuf)
@@ -686,8 +665,9 @@
 			return ret;
 
 	/* FIXME: figure out all this OSS fragment stuff */
-	bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];
+	bytepersec = dmabuf->rate * dmabuf->bps_user;
 	bufsize = PAGE_SIZE << dmabuf->buforder;
+
 	if (dmabuf->ossfragshift) {
 		if ((1000 << dmabuf->ossfragshift) < bytepersec)
 			dmabuf->fragshift = ld2(bytepersec/1000);
@@ -697,19 +677,27 @@
 		/* lets hand out reasonable big ass buffers by default */
 		dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2);
 	}
+	
+	dmabuf->ossfragshift = dmabuf->fragshift;
+							/* double or half the amount of data */
+	if (dmabuf->bps_user < dmabuf->bps_buffer)	/* write: convert from 16 bit mono to 16 stereo */
+		dmabuf->fragshift++;
+	if (dmabuf->bps_user > dmabuf->bps_buffer)	/* read: convert from 16 bit stereo to 16 mono (future) */
+		dmabuf->fragshift--;
+
 	dmabuf->numfrag = bufsize >> dmabuf->fragshift;
 	while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {
 		dmabuf->fragshift--;
 		dmabuf->numfrag = bufsize >> dmabuf->fragshift;
 	}
-	dmabuf->fragsize = 1 << 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;
+	
+	dmabuf->fragsamples = dmabuf->fragsize / dmabuf->bps_buffer; 
+	dmabuf->dmasize = dmabuf->numfrag * dmabuf->fragsize;
 
-	memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80,
-	       dmabuf->dmasize);
+	memset(dmabuf->rawbuf, 0 , dmabuf->dmasize);
 
 	/*
 	 *	Now set up the ring 
@@ -719,8 +707,8 @@
 	fragsize = bufsize / SG_LEN;
 	
 	/*
-	 *	Load up 32 sg entries and take an interrupt at half
-	 *	way (we might want more interrupts later..) 
+	 *	Load up 32 sg entries and take an interrupt at each
+	 *	step (we might want less interrupts later..) 
 	 */
 	  
 	for(i=0;i<32;i++)
@@ -730,68 +718,31 @@
 		sg->control|=CON_IOC;
 		sg++;
 	}
-	spin_lock_irqsave(&state->card->lock, flags);
-	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 (rec) {
-		i810_rec_setup(state);
-	} else {
-		i810_play_setup(state);
-	}
-	spin_unlock_irqrestore(&state->card->lock, flags);
+	spin_lock_irqsave(&card->lock, flags);
+	outb(2, card->iobase+dmabuf->channel->port + OFF_CR);	/* reset DMA machine */
+	outl(virt_to_bus(&dmabuf->channel->sg[0]), card->iobase+dmabuf->channel->port+OFF_BDBAR);
+	outb(16, card->iobase+dmabuf->channel->port + OFF_LVI);
+	outb(0, card->iobase+dmabuf->channel->port + OFF_CIV);
+	
+	dmabuf->total_bytes = 0;
+	dmabuf->count = dmabuf->error = 0;
+	resync_dma_ptrs(state);
+	
+	spin_unlock_irqrestore(&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, "
-	       "fragsize = %d dmasize = %d\n",
-	       dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,
-	       dmabuf->fragsize, dmabuf->dmasize);
+	printk("i810_audio: prog_dmabuf, sample rate = %d, bps_buffer = %d, bps_user =  %d,\n\t"
+		"numfrag = %d, fragsize = %d dmasize = %d fragshift = %d, ossfragshift = %d\n",
+	       dmabuf->rate, dmabuf->bps_buffer, dmabuf->bps_user, dmabuf->numfrag,
+	       dmabuf->fragsize, dmabuf->dmasize, dmabuf->fragshift, dmabuf->ossfragshift);
 #endif
 
 	return 0;
 }
 
-/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.
-   |------------|------------|   or   |xxxxxxxxxxxx|------------|   or   |xxxxxxxxxxxx|xxxxxxxxxxxx|
-   but we almost always get this
-   |xxxxxx------|------------|   or   |xxxxxxxxxxxx|xxxxx-------|
-   so we have to clear the tail space to "silence"
-   |xxxxxx000000|------------|   or   |xxxxxxxxxxxx|xxxxxx000000|
-*/
-static void i810_clear_tail(struct i810_state *state)
-{
-	struct dmabuf *dmabuf = &state->dmabuf;
-	unsigned swptr;
-	unsigned char silence = (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80;
-	unsigned int len;
-	unsigned long flags;
-
-	spin_lock_irqsave(&state->card->lock, flags);
-	swptr = dmabuf->swptr;
-	spin_unlock_irqrestore(&state->card->lock, flags);
-
-	if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
-		return;
-
-	if (swptr < dmabuf->dmasize/2)
-		len = dmabuf->dmasize/2 - swptr;
-	else
-		len = dmabuf->dmasize - swptr;
-
-	memset(dmabuf->rawbuf + swptr, silence, len);
-
-	spin_lock_irqsave(&state->card->lock, flags);
-	dmabuf->swptr += len;
-	dmabuf->count += len;
-	spin_unlock_irqrestore(&state->card->lock, flags);
-
-	/* restart the dma machine in case it is halted */
-	start_dac(state);
-}
-
 static int drain_dac(struct i810_state *state, int nonblock)
 {
 	DECLARE_WAITQUEUE(wait, current);
@@ -826,7 +777,7 @@
 		}
 
 		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
-		tmo >>= sample_shift[dmabuf->fmt];
+		tmo = tmo / dmabuf->bps_buffer;
 		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
 			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");
 			break;
@@ -847,15 +798,14 @@
 	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;
+	dmabuf->total_bytes += diff * dmabuf->bps_user/dmabuf->bps_buffer;
+	dmabuf->blocks +=  diff / dmabuf->fragsize;
 
 	/* error handling and process wake up for DAC */
 	if (dmabuf->enable == ADC_RUNNING) {
@@ -875,17 +825,17 @@
 			}
 			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);
+					memset (dmabuf->rawbuf + swptr, 0, clear_cnt);
 					dmabuf->endcleared = 1;
 				}
-			}			
-			wake_up(&dmabuf->wait);
+			}
+			if (dmabuf->count < (signed)dmabuf->dmasize/2)
+				wake_up(&dmabuf->wait);
 		}
 	}
 	/* error handling and process wake up for DAC */
@@ -902,10 +852,14 @@
 				   it here, just stop the machine and let the process force hwptr
 				   and swptr to sync */
 				__stop_dac(state);
-				printk("DMA overrun on send\n");
+				if (dmabuf->count > dmabuf->dmasize)
+				       	printk(KERN_WARNING "i810_audio: DMA overrun on send.\n");
+				else
+					printk(KERN_WARNING "i810_audio: DMA underrun on send.\n");
 				dmabuf->error++;
 			}
-			wake_up(&dmabuf->wait);
+			if (dmabuf->count < (signed)dmabuf->dmasize/2)
+				wake_up(&dmabuf->wait);
 		}
 	}
 }
@@ -1038,7 +992,7 @@
 			}
 			/* This isnt strictly right for the 810  but it'll do */
 			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
+			tmo = tmo / dmabuf->bps_buffer;
 			/* 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
@@ -1122,8 +1076,6 @@
 			cnt = dmabuf->dmasize - dmabuf->count;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
-		if (cnt > count)
-			cnt = count;
 		if (cnt <= 0) {
 			unsigned long tmo;
 			/* buffer is full, start the dma machine and wait for data to be
@@ -1135,7 +1087,7 @@
 			}
 			/* Not strictly correct but works */
 			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= sample_shift[dmabuf->fmt];
+			tmo = tmo / dmabuf->bps_buffer;
 			/* 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
@@ -1159,19 +1111,54 @@
 			}
 			continue;
 		}
-		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
-			if (!ret) ret = -EFAULT;
-			return ret;
-		}
-
-		swptr = (swptr + cnt) % dmabuf->dmasize;
-
-		spin_lock_irqsave(&state->card->lock, flags);
-		dmabuf->swptr = swptr;
-		dmabuf->count += cnt;
-		dmabuf->endcleared = 0;
-		spin_unlock_irqrestore(&state->card->lock, flags);
 
+		if (dmabuf->bps_buffer > dmabuf->bps_user) {
+			/* this can only mean that we have  to convert from mono to stereo */
+			int i;
+			unsigned short *p, *q;
+			/* We want to write count bytes and have room for cnt bytes,
+			 * however because the i810 can do stereo only we have to duplicate
+			 * all samples, so we really only have room for cnt/2 bytes. */
+			cnt = cnt/2;
+			if (cnt > count)
+				cnt = count;
+			/*  we do 16 bits only */
+			if (cnt < 2)
+				break;
+			if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+				if (!ret) ret = -EFAULT;
+				return ret;
+			}
+			/* duplicate the samples */
+			p = dmabuf->rawbuf + swptr;
+			q = p + cnt/2 - 1;
+			p = p + cnt - 1;
+			for (i=cnt/2; --i;) {
+				*p-- = *q;
+				*p-- = *q--;
+			}
+			swptr = (swptr + cnt*2) % dmabuf->dmasize;
+			spin_lock_irqsave(&state->card->lock, flags);
+			dmabuf->swptr = swptr;
+			dmabuf->count += cnt*2;
+			dmabuf->endcleared = 0;
+			spin_unlock_irqrestore(&state->card->lock, flags);
+		}
+		else
+		{
+			if (cnt > count)
+				cnt = count;
+			if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {
+				if (!ret) ret = -EFAULT;
+				return ret;
+			}
+			swptr = (swptr + cnt) % dmabuf->dmasize;
+			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;
@@ -1186,12 +1173,18 @@
 	struct i810_state *state = (struct i810_state *)file->private_data;
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned long flags;
-	unsigned int mask = 0;
+	unsigned int mask = 0, ret;
 
-	if (file->f_mode & FMODE_WRITE)
+	if (file->f_mode & FMODE_WRITE) {
+		if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
+			return ret;
 		poll_wait(file, &dmabuf->wait, wait);
-	if (file->f_mode & FMODE_READ)
+	}
+	if (file->f_mode & FMODE_READ) {
+		if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
+			return ret;
 		poll_wait(file, &dmabuf->wait, wait);
+	}
 
 	spin_lock_irqsave(&state->card->lock, flags);
 	i810_update_ptr(state);
@@ -1231,6 +1224,9 @@
 		goto out;
 
 	ret = -EINVAL;
+	if (dmabuf->bps_user != dmabuf->bps_buffer)
+		goto out;
+
 	if (vma->vm_pgoff != 0)
 		goto out;
 	size = vma->vm_end - vma->vm_start;
@@ -1274,17 +1270,15 @@
 			stop_dac(state);
 			synchronize_irq();
 			dmabuf->ready = 0;
-			resync_dma_ptrs(state);
 			dmabuf->swptr = dmabuf->hwptr = 0;
-			dmabuf->count = dmabuf->total_bytes = 0;
+			dmabuf->count = dmabuf->total_bytes = dmabuf->blocks = 0;
 		}
 		if (file->f_mode & FMODE_READ) {
 			stop_adc(state);
 			synchronize_irq();
-			resync_dma_ptrs(state);
 			dmabuf->ready = 0;
 			dmabuf->swptr = dmabuf->hwptr = 0;
-			dmabuf->count = dmabuf->total_bytes = 0;
+			dmabuf->count = dmabuf->total_bytes = dmabuf->blocks = 0;
 		}
 		return 0;
 
@@ -1293,7 +1287,7 @@
 			return drain_dac(state, file->f_flags & O_NONBLOCK);
 		return 0;
 
-	case SNDCTL_DSP_SPEED: /* set smaple rate */
+	case SNDCTL_DSP_SPEED: /* set sample rate */
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		if (val >= 0) {
@@ -1317,30 +1311,39 @@
 	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-		if(val==0)
-			return -EINVAL;
 		if (file->f_mode & FMODE_WRITE) {
+			if ((val==0) && !dmabuf->mapped) {
+				dmabuf->bps_user  = 2;
+				dmabuf->bps_buffer = 4;
+			} else {
+				dmabuf->bps_user  = 4;
+				dmabuf->bps_buffer = 4;
+				val = 1;
+			}
 			stop_dac(state);
 			dmabuf->ready = 0;
-			dmabuf->fmt = I810_FMT_STEREO;
 		}
 		if (file->f_mode & FMODE_READ) {
+			/* READ is currently always stereo */
 			stop_adc(state);
 			dmabuf->ready = 0;
-			dmabuf->fmt = I810_FMT_STEREO;
+			dmabuf->bps_user  = 4;
+			dmabuf->bps_buffer = 4;
+			val = 1;
 		}
+		put_user(val, (int *)arg);
 		return 0;
 
 	case SNDCTL_DSP_GETBLKSIZE:
 		if (file->f_mode & FMODE_WRITE) {
 			if ((val = prog_dmabuf(state, 0)))
 				return val;
-			return put_user(dmabuf->fragsize, (int *)arg);
+			return put_user(dmabuf->fragsamples * dmabuf->bps_user, (int *)arg);
 		}
 		if (file->f_mode & FMODE_READ) {
 			if ((val = prog_dmabuf(state, 1)))
 				return val;
-			return put_user(dmabuf->fragsize, (int *)arg);
+			return put_user(dmabuf->fragsamples * dmabuf->bps_user, (int *)arg);
 		}
 
 	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
@@ -1368,13 +1371,24 @@
 			if (file->f_mode & FMODE_WRITE) {
 				stop_dac(state);
 				dmabuf->ready = 0;
+				if ((val > 1) || dmabuf->mapped) {
+					dmabuf->bps_user = 4;
+					val = 2;
+				} else {
+					dmabuf->bps_user = 2;
+				}
+				dmabuf->bps_buffer = 4;
 			}
 			if (file->f_mode & FMODE_READ) {
+				/* READ is currently always stereo */
 				stop_adc(state);
 				dmabuf->ready = 0;
+				dmabuf->bps_user  = 4;
+				dmabuf->bps_buffer = 4;
+				val = 2;
 			}
 		}
-		return put_user(2, (int *)arg);
+		return put_user(val > 1 ? 2 : 1, (int *)arg);
 
 	case SNDCTL_DSP_POST:
 		/* FIXME: the same as RESET ?? */
@@ -1408,28 +1422,28 @@
 	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);
-		abinfo.fragsize = dmabuf->fragsize;
-		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
+		abinfo.bytes = (dmabuf->dmasize - dmabuf->count) * dmabuf->bps_user / dmabuf->bps_buffer;
+		abinfo.fragsize = dmabuf->fragsamples * dmabuf->bps_user;
 		abinfo.fragstotal = dmabuf->numfrag;
-		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		abinfo.fragments = abinfo.bytes >> dmabuf->ossfragshift;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
 
 	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);
-		abinfo.fragsize = dmabuf->fragsize;
-		abinfo.bytes = dmabuf->count;
+		abinfo.bytes = dmabuf->count * dmabuf->bps_user / dmabuf->bps_buffer;
+		abinfo.fragsize = dmabuf->fragsamples * dmabuf->bps_user;
 		abinfo.fragstotal = dmabuf->numfrag;
-		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
+		abinfo.fragments = abinfo.bytes >> dmabuf->ossfragshift;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
 
@@ -1438,7 +1452,7 @@
 		return 0;
 
 	case SNDCTL_DSP_GETCAPS:
-	    return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
+	    return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
 			    (int *)arg);
 
 	case SNDCTL_DSP_GETTRIGGER:
@@ -1473,10 +1487,14 @@
 	case SNDCTL_DSP_GETIPTR:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
+		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
+			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
 		i810_update_ptr(state);
 		cinfo.bytes = dmabuf->total_bytes;
-		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.blocks = dmabuf->blocks;
+		dmabuf->blocks = 0;
+		/* this field is useless */
 		cinfo.ptr = dmabuf->hwptr;
 		if (dmabuf->mapped)
 			dmabuf->count &= dmabuf->fragsize-1;
@@ -1486,10 +1504,14 @@
 	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.blocks = dmabuf->count >> dmabuf->fragshift;
+		cinfo.blocks = dmabuf->blocks;
+		dmabuf->blocks = 0;
+		/* this field is useless */
 		cinfo.ptr = dmabuf->hwptr;
 		if (dmabuf->mapped)
 			dmabuf->count &= dmabuf->fragsize-1;
@@ -1502,6 +1524,8 @@
 	case SNDCTL_DSP_GETODELAY:
 		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);
 		val = dmabuf->count;
@@ -1512,8 +1536,7 @@
 		return put_user(dmabuf->rate, (int *)arg);
 
 	case SOUND_PCM_READ_CHANNELS:
-		return put_user((dmabuf->fmt & I810_FMT_STEREO) ? 2 : 1,
-				(int *)arg);
+		return put_user(2, (int *)arg);
 
 	case SOUND_PCM_READ_BITS:
 		return put_user(AFMT_S16_LE, (int *)arg);
@@ -1531,7 +1554,6 @@
 static int i810_open(struct inode *inode, struct file *file)
 {
 	int i = 0;
-	int minor = MINOR(inode->i_rdev);
 	struct i810_card *card = devs;
 	struct i810_state *state = NULL;
 	struct dmabuf *dmabuf = NULL;
@@ -1579,29 +1601,26 @@
 	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 */
+	   should be default to unsigned 8-bits, mono, with sample rate 8kHz .
+	   But not if the hardware does not support this format, so we default
+	   to 48kHz, stereo 16 bit. All codecs support this. */
+	dmabuf->bps_user  = 4;
+	dmabuf->bps_buffer = 4;
+	dmabuf->ossfragshift = 0;
+	dmabuf->ossmaxfrags  = 0;
+	dmabuf->subdivision  = 0;
 	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);
 	}
 
 	state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
 	up(&state->open_sem);
 
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -1611,10 +1630,8 @@
 	struct dmabuf *dmabuf = &state->dmabuf;
 
 	lock_kernel();
-	if (file->f_mode & FMODE_WRITE) {
-		i810_clear_tail(state);
+	if (file->f_mode & FMODE_WRITE)
 		drain_dac(state, file->f_flags & O_NONBLOCK);
-	}
 
 	/* stop DMA state machine and free DMA buffers/channels */
 	down(&state->open_sem);
@@ -1636,7 +1653,7 @@
 	kfree(state->card->states[state->virt]);
 	state->card->states[state->virt] = NULL;
 	unlock_kernel();
-
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1661,6 +1678,8 @@
 
 	while(count-- && (inb(card->iobase + CAS) & 1)) 
 		udelay(1);
+	if(!count)
+		printk(KERN_ERR "i810_audio: AC97 access failed.\n");
 	return inw(card->ac97base + (reg&0x7f));
 }
 
@@ -1671,6 +1690,8 @@
 
 	while(count-- && (inb(card->iobase + CAS) & 1)) 
 		udelay(1);
+	if(!count)
+		printk(KERN_ERR "i810_audio: AC97 write access failed.\n");
 	outw(data, card->ac97base + (reg&0x7f));
 }
 
@@ -1695,6 +1716,13 @@
  match:
 	file->private_data = card->ac97_codec[i];
 
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int i810_release_mixdev(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1711,6 +1739,7 @@
 	llseek:		i810_llseek,
 	ioctl:		i810_ioctl_mixdev,
 	open:		i810_open_mixdev,
+	release:	i810_release_mixdev,
 };
 
 /* AC97 codec initialisation. */
@@ -1723,6 +1752,7 @@
 	int i=0;
 	u32 reg;
 
+	
 	reg = inl(card->iobase + GLOB_CNT);
 	
 	if((reg&2)==0)	/* Cold required */
@@ -1746,7 +1776,7 @@
 		printk(KERN_ERR "i810_audio: AC'97 reset failed.\n");
 		return 0;
 	}
-
+ 
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ/5);
 		
@@ -1768,6 +1798,9 @@
 		if (ac97_probe_codec(codec) == 0)
 			break;
 
+		/* Now check the codec for useful features to make up for 
+		   the dumbness of the 810 hardware engine */
+
 		eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
 		
 		if(eid==0xFFFFFF)
@@ -1777,10 +1810,36 @@
 			break;
 		}
 		
+		
 		card->ac97_features = eid;
 				
-		if(!(eid&0x0001))
+		if (!(eid&0x0001))
 			printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
+		else
+		{
+			/* In the AD1885 you cannot enable VRA when
+			 * the analog sections are not yet ready */
+			for (i=10; i--;)
+			{
+				if ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) == 0xf)
+					break;
+				current->state = TASK_UNINTERRUPTIBLE;
+				schedule_timeout(HZ/10);
+			}
+			if (i == 0)
+				printk(KERN_WARNING "i810_audio: Analog subsections not ready.\n");
+
+			/* 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 (!((i = i810_ac97_get(codec, AC97_EXTENDED_STATUS))&1))
+			{
+				printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only %04x.\n", i);
+				card->ac97_features&=~1;
+			}
+		}
 			
 		if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
 			printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
@@ -1788,9 +1847,6 @@
 			break;
 		}
 
-		/* Now check the codec for useful features to make up for 
-		   the dumbness of the 810 hardware engine */
-		   
 		card->ac97_codec[num_ac97] = codec;
 
 		/* if there is no secondary codec at all, don't probe any more */
@@ -1904,7 +1960,7 @@
 }
 
 
-MODULE_AUTHOR("");
+MODULE_AUTHOR("Assorted");
 MODULE_DESCRIPTION("Intel 810 audio support");
 MODULE_PARM(ftsodell, "i");
 MODULE_PARM(clocking, "i");
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/

             reply	other threads:[~2000-12-01 11:51 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2000-12-01 11:18 Tjeerd Mulder [this message]
2000-12-01 13:02 ` [PATCH] i810_audio 2.4.0-test11 Alan Cox
2000-12-03 21:23   ` Pavel Machek
2000-12-04 11:37     ` Alan Cox
2000-12-04 13:45       ` Thomas Sailer
2000-12-04 16:37         ` Jeff Garzik
2000-12-04 18:15         ` Alan Cox
2000-12-04 18:23           ` Thomas Sailer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3A278916.6FF0C5DE@fujitsu-siemens.com \
    --to=tjeerd.mulder@fujitsu-siemens.com \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@transmeta.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).