Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 11 Dec 1999 18:55:38 +0900
From:      Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp>
To:        current@freebsd.org
Cc:        Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp>
Subject:   The secondary buffer in pcm channel for DSPs with small DMA buffers
Message-ID:  <14418.8090.86742.72159A@silver.carrots.uucp.r.dl.itc.u-tokyo.ac.jp>

next in thread | raw e-mail | index | archive | help
--Multipart_Sat_Dec_11_18:55:37_1999-1
Content-Type: text/plain; charset=US-ASCII

My CS4614 has got only 4KB of the DMA buffer, frequently failing to
play pcm blocks continuously. The following patch adds the secondary
pcm channel buffers with the size independent from that of a DSP. The
patch works quite well with mpg123 and x11amp. For those who has the
similar problem could you please try that?



--Multipart_Sat_Dec_11_18:55:37_1999-1
Content-Type: text/plain; type=patch; charset=US-ASCII
Content-Disposition: attachment; filename="2ndbuffer.diff"
Content-Transfer-Encoding: 7bit

Index: channel.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/channel.c,v
retrieving revision 1.9
diff -u -r1.9 channel.c
--- channel.c	1999/12/05 19:09:12	1.9
+++ channel.c	1999/12/11 09:34:25
@@ -33,6 +33,13 @@
 #define	DMA_ALIGN_THRESHOLD	4
 #define	DMA_ALIGN_MASK		(~(DMA_ALIGN_THRESHOLD - 1))
 
+/*
+ * This should be large enough to hold all pcm data between
+ * tsleeps in chn_{read,write} at the highest sample rate.
+ * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec)
+ */
+#define CHN_2NDBUFSIZE			(12 * 1024)
+
 #define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8))
 #define CANCHANGE(c) (!(c)->buffer.dl)
 
@@ -131,9 +138,8 @@
 }
 
 /*
- * chn_dmadone() updates pointers and wakes up any process sleeping
- * or waiting on a select().
- * Must be called at spltty().
+ * chn_dmadone() updates pointers and wakes up any process waiting
+ * on a select(). Must be called at spltty().
  */
 static void
 chn_dmadone(pcm_channel *c)
@@ -142,12 +148,23 @@
 
 	chn_dmaupdate(c);
 	if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */
-	wakeup(b);
 	b->int_count++;
 	if (b->sel.si_pid && chn_polltrigger(c)) selwakeup(&b->sel);
 }
 
 /*
+ * chn_dmawakeup() wakes up any process sleeping.
+ * Must be called at spltty().
+ */
+static void
+chn_dmawakeup(pcm_channel *c)
+{
+	snd_dbuf *b = &c->buffer;
+
+	wakeup(b);
+}
+
+/*
  * chn_dmaupdate() tracks the status of a dma transfer,
  * updating pointers. It must be called at spltty().
  *
@@ -181,8 +198,9 @@
 }
 
 /*
- * Check channel for underflow occured, reset DMA buffer in case of
- * underflow. It must be called at spltty().
+ * Check channel for underflow occured. Reset DMA buffer in case of
+ * underflow, so that new data can go into the buffer. It must be
+ * called at spltty().
  */
 static void
 chn_checkunderflow(pcm_channel *c)
@@ -201,6 +219,61 @@
 }
 
 /*
+ * Feeds new data to the write dma buffer. Can be called in the bottom half.
+ * Must be called at spltty.
+ */
+static int
+chn_wrfeed(pcm_channel *c)
+{
+    	snd_dbuf *b = &c->buffer;
+    	snd_dbuf *bs = &c->buffer2nd;
+	int a, l, lacc;
+
+	/* ensure we always have a whole number of samples */
+	a = (1 << c->align) - 1;
+	lacc = 0;
+	/* Don't allow write unaligned data */
+	while (bs->rl > a && b->fl > a) {
+		/* ensure we always have a whole number of samples */
+		l = min(min(bs->rl, bs->bufsize - bs->rp), min(b->fl, b->bufsize - b->fp)) & ~a;
+		if (l == 0)
+			return lacc;
+		bcopy(bs->buf + bs->rp, b->buf + b->fp, l);
+		bs->fl += l;
+		bs->rl -= l;
+		bs->rp = (bs->rp + l) % bs->bufsize;
+		b->rl += l;
+		b->fl -= l;
+		b->fp = (b->fp + l) % b->bufsize;
+		lacc += l;
+	}
+
+	return lacc;
+}
+
+/* Feeds new data to the secondary write buffer. */
+static int
+chn_wrfeed2nd(pcm_channel *c, struct uio *buf)
+{
+    	snd_dbuf *bs = &c->buffer2nd;
+	int l, w, wacc;
+
+	/* ensure we always have a whole number of samples */
+	wacc = 0;
+	while (buf->uio_resid > 0 && bs->fl > 0) {
+		l = min(bs->fl, bs->bufsize - bs->fp);
+		w = c->feeder->feed(c->feeder, c, bs->buf + bs->fp, l, buf);
+		if (w == 0) panic("no feed");
+		bs->rl += w;
+		bs->fl -= w;
+		bs->fp = (bs->fp + w) % bs->bufsize;
+		wacc += w;
+	}
+
+	return wacc;
+}
+
+/*
  * Write interrupt routine. Can be called from other places (e.g.
  * to start a paused transfer), but with interrupts disabled.
  */
@@ -222,7 +295,14 @@
      	* needed when doing stereo and 16-bit.
      	*/
     	if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
-    	else start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
+    	else {
+		/* Fill up the DMA buffer. */
+		if (chn_wrfeed(c) > 0) {
+			chn_dmawakeup(c);
+			while(chn_wrfeed(c) > 0);
+		}
+		start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
+	}
     	if (start) {
 		int l;
 		chn_dmaupdate(c);
@@ -237,13 +317,13 @@
 			/* Start DMA operation */
 	    		b->dl = c->blocksize ; /* record new transfer size */
 	    		chn_trigger(c, PCMTRIG_START);
-		} else if (b->dl != l) {
+		}
+		if (b->dl != l)
 			/*
 			 * we are near to underflow condition, so to prevent
 			 * audio 'clicks' clear next 1.5*dl bytes
 			 */
 			 chn_clearbuf(c, (b->dl*3)/2);
-		}
     	} else {
 		/* cannot start a new dma transfer */
 		DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
@@ -274,10 +354,9 @@
 int
 chn_write(pcm_channel *c, struct uio *buf)
 {
-	int 		a, l, w, timeout, ret = 0, rc;
+	int 		ret = 0, timeout;
 	long		s;
 	snd_dbuf       *b = &c->buffer;
-	int		threshold, maxthreshold, minthreshold;
 
 	if (c->flags & CHN_F_WRITING) {
 		/* This shouldn't happen and is actually silly
@@ -286,57 +365,36 @@
 	       	tsleep(&s, PZERO, "pcmwrW", hz);
 		return EBUSY;
 	}
-	a = (1 << c->align) - 1;
-	maxthreshold = (b->dl / 4 + a) & ~a;
-	minthreshold = a;
 	c->flags |= CHN_F_WRITING;
+	/*
+	 * Fill up the secondary and DMA buffer.
+	 * chn_wrfeed*() takes care of the alignment.
+	 */
 	s = spltty();
 	chn_checkunderflow(c);
-	splx(s);
-	while ((buf->uio_resid + c->smegcnt) > minthreshold ) { /* Don't allow write unaligned data */
-		threshold = min((buf->uio_resid + c->smegcnt), maxthreshold);
-		if (b->fl < threshold) {
-			if (c->flags & CHN_F_NBIO) {
-				ret = -1;
-				break;
-			}
-			timeout = (buf->uio_resid >= b->dl)? hz : 1;
-			rc = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
-			if (rc == 0 || rc == EWOULDBLOCK) {
-				s = spltty();
-				chn_checkunderflow(c);
-				splx(s);
-				if (b->fl < minthreshold) continue; /* write only alligned chunk of data */
-			} else {
-#if 0
-			 	if (ret == EINTR) chn_abort(c);
-#endif
-				ret = rc;
-				break;
-			}
-		}
-		/* ensure we always have a whole number of samples */
-		l = min(b->fl, b->bufsize - b->fp) & ~a;
-		if (l == 0) continue;
-		w = c->feeder->feed(c->feeder, c, b->buf + b->fp, l, buf);
-		KASSERT(w, ("chn_write: no feed"));
-		s = spltty();
-		b->rl += w;
-		b->fl -= w;
-		b->fp = (b->fp + w) % b->bufsize;
-	      	splx(s);
-		DEB(if(1) printf("write %d bytes fp %d rl %d\n",w ,b->fp, b->rl));
-		if (!b->dl) chn_stintr(c);
-	}
-	if ((ret == 0) && (buf->uio_resid > 0)) {
-		s = spltty();
-		l = buf->uio_resid;
-		KASSERT( (c->smegcnt + l) < SMEGBUFSZ, ("resid overflow %d", l));
-		uiomove(c->smegbuf + c->smegcnt, l, buf);
-		c->smegcnt += l;
-		splx(s);
-	}
+  	while (chn_wrfeed2nd(c, buf) > 0 || chn_wrfeed(c) > 0);
+	/* Start playing if not yet. */
+	if (b->rl && !b->dl) chn_wrintr(c);
+   	if (!(c->flags & CHN_F_NBIO)) {
+   		/* Wait until all samples are played in blocking mode. */
+   		while (buf->uio_resid > 0) {
+			splx(s);
+			timeout = (buf->uio_resid >= b->dl)? hz / 20 : 1;
+   			ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
+   			s = spltty();
+#if notdef
+ 			if (ret == EINTR) chn_abort(c);
+#endif /* notdef */
+ 			if (ret == EINTR || ret == ERESTART) break;
+			chn_checkunderflow(c);
+			/* Fill up the buffers with new pcm data. */
+  			while (chn_wrfeed2nd(c, buf) > 0 || chn_wrfeed(c) > 0);
+			/* Start playing if necessary. */
+  			if (b->rl && !b->dl) chn_wrintr(c);
+ 		}
+  	}
 	c->flags &= ~CHN_F_WRITING;
+   	splx(s);
 	return (ret > 0)? ret : 0;
 }
 
@@ -370,6 +428,56 @@
 
  */
 
+/*
+ * Feed new data from the read buffer. Can be called in the bottom half.
+ * Must be called at spltty.
+ */
+static int
+chn_rdfeed(pcm_channel *c)
+{
+    	snd_dbuf *b = &c->buffer;
+    	snd_dbuf *bs = &c->buffer2nd;
+	int l, lacc;
+
+	/* ensure we always have a whole number of samples */
+	lacc = 0;
+	while (bs->fl >= DMA_ALIGN_THRESHOLD && b->rl >= DMA_ALIGN_THRESHOLD) {
+		l = min(min(bs->fl, bs->bufsize - bs->fp), min(b->rl, b->bufsize - b->rp)) & DMA_ALIGN_MASK;
+		bcopy(b->buf + b->rp, bs->buf + bs->fp, l);
+		bs->fl -= l;
+		bs->rl += l;
+		bs->fp = (bs->fp + l) % bs->bufsize;
+		b->rl -= l;
+		b->fl += l;
+		b->rp = (b->rp + l) % b->bufsize;
+		lacc += l;
+	}
+
+	return lacc;
+}
+
+/* Feeds new data from the secondary read buffer. */
+static int
+chn_rdfeed2nd(pcm_channel *c, struct uio *buf)
+{
+    	snd_dbuf *bs = &c->buffer2nd;
+	int l, w, wacc;
+
+	/* ensure we always have a whole number of samples */
+	wacc = 0;
+	while (buf->uio_resid > 0 && bs->rl > 0) {
+		l = min(bs->rl, bs->bufsize - bs->rp);
+		w = c->feeder->feed(c->feeder, c, bs->buf + bs->rp, l, buf);
+		if (w == 0) panic("no feed");
+		bs->fl += w;
+		bs->rl -= w;
+		bs->rp = (bs->rp + w) % bs->bufsize;
+		wacc += w;
+	}
+
+	return wacc;
+}
+
 /* read interrupt routine. Must be called with interrupts blocked. */
 static void
 chn_rdintr(pcm_channel *c)
@@ -383,7 +491,14 @@
 		b->dl, b->rp, b->rl, b->fp, b->fl));
     	/* Restart if have enough free space to absorb overruns */
     	if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
-    	else start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
+    	else {
+		/* Suck up the DMA buffer. */
+		if (chn_rdfeed(c) > 0) {
+			chn_dmawakeup(c);
+			while (chn_rdfeed(c) > 0);
+		}
+		start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
+	}
     	if (start) {
 		int l = min(b->fl - 0x100, c->blocksize);
 		if (c->flags & CHN_F_MAPPED) l = c->blocksize;
@@ -431,7 +546,7 @@
 int
 chn_read(pcm_channel *c, struct uio *buf)
 {
-	int		w, l, timeout, limit, ret = 0;
+	int		ret = 0, timeout, limit;
 	long		s;
 	snd_dbuf       *b = &c->buffer;
 
@@ -441,33 +556,31 @@
 		return (EBUSY);
 	}
 
-	if (!b->rl & !b->dl) chn_stintr(c);
+  	s = spltty();
 	c->flags |= CHN_F_READING;
 	limit = buf->uio_resid - c->blocksize;
 	if (limit < 0) limit = 0;
-	while (buf->uio_resid > limit) {
-		s = spltty();
-		chn_dmaupdate(c);
-		splx(s);
-		if (b->rl < DMA_ALIGN_THRESHOLD) {
-			if (c->flags & CHN_F_NBIO) break;
-			timeout = (buf->uio_resid - limit >= b->dl)? hz : 1;
-			ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
+	/* Start capturing if not yet. */
+  	if (!b->rl & !b->dl) chn_rdintr(c);
+	/* Suck up the DMA and secondary buffers. */
+ 	while (chn_rdfeed(c) > 0 || chn_rdfeed2nd(c, buf) > 0);
+  	if (!(c->flags & CHN_F_NBIO)) {
+  		/* Wait until all samples are captured. */
+  		while (buf->uio_resid > 0) {
+			splx(s);
+			timeout = (buf->uio_resid - limit >= b->dl)? hz / 20 : 1;
+  			ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
+  			s = spltty();
 			if (ret == EINTR) chn_abort(c);
 			if (ret == EINTR || ret == ERESTART) break;
-			ret = 0;
-			continue;
+			/* Start capturing if necessary. */
+ 			if (!b->rl & !b->dl) chn_rdintr(c);
+			/* Suck up the DMA and secondary buffers. */
+ 			while (chn_rdfeed(c) > 0 || chn_rdfeed2nd(c, buf) > 0);
 		}
-		/* ensure we always have a whole number of samples */
-		l = min(b->rl, b->bufsize - b->rp) & DMA_ALIGN_MASK;
-		w = c->feeder->feed(c->feeder, c, b->buf + b->rp, l, buf);
-		s = spltty();
-		b->rl -= w;
-		b->fl += w;
-		b->rp = (b->rp + w) % b->bufsize;
-	      	splx(s);
 	}
 	c->flags &= ~CHN_F_READING;
+  	splx(s);
 	return ret;
 }
 
@@ -534,6 +647,7 @@
 chn_resetbuf(pcm_channel *c)
 {
 	snd_dbuf *b = &c->buffer;
+	snd_dbuf *bs = &c->buffer2nd;
 
 	c->smegcnt = 0;
 	c->buffer.sample_size = 1;
@@ -547,6 +661,9 @@
 	b->prev_int_count = b->int_count = 0;
 	b->first_poll = 1;
 	b->underflow=0;
+	bs->rp = bs->fp = 0;
+	bs->dl = bs->rl = 0;
+	bs->fl = bs->bufsize;
 }
 
 void
@@ -631,6 +748,7 @@
     	long s;
     	int missing = 0;
     	snd_dbuf *b = &c->buffer;
+    	snd_dbuf *bs = &c->buffer2nd;
 
     	s = spltty();
     	if (b->dl) {
@@ -639,7 +757,7 @@
 		chn_trigger(c, PCMTRIG_ABORT);
 		chn_dmadone(c);
     	}
-    	missing = b->rl;
+    	missing = b->rl + bs->rl;
     	splx(s);
     	return missing;
 }
@@ -708,11 +826,18 @@
 int
 chn_init(pcm_channel *c, void *devinfo, int dir)
 {
+	snd_dbuf       *bs = &c->buffer2nd;
+
 	c->flags = 0;
 	c->feeder = &feeder_root;
 	c->buffer.chan = -1;
 	c->devinfo = c->init(devinfo, &c->buffer, c, dir);
 	chn_setdir(c, dir);
+	bs->bufsize = CHN_2NDBUFSIZE;
+	bs->buf = malloc(bs->bufsize, M_DEVBUF, M_NOWAIT);
+	bzero(bs->buf, bs->bufsize);
+	bs->rl = bs->rp = bs->fp = 0;
+	bs->fl = bs->bufsize;
 	return 0;
 }
 
Index: datatypes.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/datatypes.h,v
retrieving revision 1.5
diff -u -r1.5 datatypes.h
--- datatypes.h	1999/12/05 19:09:12	1.5
+++ datatypes.h	1999/12/11 09:34:25
@@ -127,6 +127,7 @@
 	u_int8_t smegbuf[SMEGBUFSZ];
 	u_int32_t smegcnt;
 	void *devinfo;
+	snd_dbuf buffer2nd;
 };
 
 #define SND_STATUSLEN	64

-- 
Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp> <tanimura@freebsd.org>

--Multipart_Sat_Dec_11_18:55:37_1999-1--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?14418.8090.86742.72159A>