Subject: port-macppc/35204: snapper(4) does not set sampling rates
To: None <port-macppc-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: None <marco@ununoctium.homeunix.org>
List: netbsd-bugs
Date: 12/08/2006 00:25:01
>Number:         35204
>Category:       port-macppc
>Synopsis:       snapper(4) does not set sampling rates
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-macppc-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Dec 08 00:25:00 +0000 2006
>Originator:     marcotrillo@gmail.com
>Release:        NetBSD 3.1
>Organization:
>Environment:
System: NetBSD ununoctium.homeunix.org 3.1 NetBSD 3.1 (UNUNHEXIUM) #12: Thu Dec 7 23:47:51 CET 2006 marco@ununoctium.homeunix.org:/usr/src/sys/arch/macppc/compile/UNUNHEXIUM macppc
Architecture: powerpc
Machine: macppc
>Description:
	When playing an audio file at a different sampling rate than 44100,
	snapper(4) plays with a speed of 44100 ignoring the real rate.

	However, the audio hardware supports many different rates (8000,
	32000, 44100 and 48000).

	The problem is that the snapper_set_rate() stuff does not 
	stop and restart the clock to get the new speed.
>How-To-Repeat:
	Play an audio file at a different sampling rate.
	For some sample files, see:
		http://mail-index.netbsd.org/port-macppc/2006/12/07/0000.html
>Fix:
	The following patch should fix the problem.
	It works for me on NetBSD 3.1, should work on -current too.

	I have only tested audio output, although input should work too.

	I got the patch from Tsubai Masanari's latest versions of snapper.c
	and i2s.c, available at the CVS repository:
		http://cvsweb.ki.nu/mef/macppc/sys/arch/macppc/dev/

^^^^^^^^^^^^^^^^^ cut here ^^^^^^^^^^^^^^^^^^^^^^^^
Index: snapper.c
===================================================================
RCS file: /cvsroot/src/sys/arch/macppc/dev/snapper.c,v
retrieving revision 1.13
diff -u -r1.13 snapper.c
--- snapper.c	24 Sep 2006 03:47:09 -0000	1.13
+++ snapper.c	7 Dec 2006 23:02:40 -0000
@@ -1,8 +1,9 @@
 /*	$NetBSD: snapper.c,v 1.13 2006/09/24 03:47:09 jmcneill Exp $	*/
 /*	Id: snapper.c,v 1.11 2002/10/31 17:42:13 tsubai Exp	*/
+/*	Id: i2s.c,v 1.12 2005/01/15 14:32:35 tsubai Exp		*/
 
 /*-
- * Copyright (c) 2002 Tsubai Masanari.  All rights reserved.
+ * Copyright (c) 2002,2003 Tsubai Masanari.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,6 +78,7 @@
 	i2c_addr_t sc_deqaddr;
 	i2c_tag_t sc_i2c;
 
+	int sc_rate;			/* current sampling rate */
 	u_int sc_vol_l;
 	u_int sc_vol_r;
 	u_int sc_treble;
@@ -113,7 +115,7 @@
 int snapper_trigger_input(void *, void *, void *, int, void (*)(void *),
     void *, const audio_params_t *);
 void snapper_set_volume(struct snapper_softc *, int, int);
-int snapper_set_rate(struct snapper_softc *, u_int);
+int snapper_set_rate(struct snapper_softc *);
 void snapper_set_treble(struct snapper_softc *, int);
 void snapper_set_bass(struct snapper_softc *, int);
 void snapper_write_mixers(struct snapper_softc *);
@@ -392,7 +394,7 @@
 #define SNAPPER_NFORMATS	1
 static const struct audio_format snapper_formats[SNAPPER_NFORMATS] = {
 	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_BE, 16, 16,
-	 2, AUFMT_STEREO, 3, {8000, 44100, 48000}},
+	 2, AUFMT_STEREO, 4, {8000, 32000, 44100, 48000}},
 };
 
 static u_char *amp_mute;
@@ -409,6 +411,16 @@
 #define I2S_FRAMEMATCH	0x50
 #define I2S_WORDSIZE	0x60
 
+/* I2S_INT register definitions */
+#define I2S_INT_CLKSTOPPEND 0x01000000  /* clock-stop interrupt pending */
+
+/* FCR(0x3c) bits */
+#define I2S0CLKEN	0x1000
+#define I2S0EN		0x2000
+#define I2S1CLKEN	0x080000
+#define I2S1EN		0x100000
+#define FCR3C_BITMASK "\020\25I2S1EN\24I2S1CLKEN\16I2S0EN\15I2S0CLKEN"
+
 /* TAS3004 registers */
 #define DEQ_MCR1	0x01	/* Main control register 1 (1byte) */
 #define DEQ_DRC		0x02	/* Dynamic range compression (6bytes?) */
@@ -782,8 +794,8 @@
 	}
 
 	/* Set the speed. p points HW encoding. */
-	if (snapper_set_rate(sc, p->sample_rate))
-		return EINVAL;
+	if (p)
+		sc->sc_rate = p->sample_rate;
 
 	return 0;
 }
@@ -1120,9 +1132,14 @@
 	struct dbdma_command *cmd;
 	vaddr_t va;
 	int i, len, intmode;
+	int res;
 
 	DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize);
 	sc = h;
+
+	if ((res = snapper_set_rate(sc)) != 0)
+		return res;
+
 	cmd = sc->sc_odmacmd;
 	sc->sc_ointr = intr;
 	sc->sc_oarg = arg;
@@ -1170,9 +1187,14 @@
 	struct dbdma_command *cmd;
 	vaddr_t va;
 	int i, len, intmode;
+	int res;
 
 	DPRINTF("trigger_input %p %p 0x%x\n", start, end, bsize);
 	sc = h;
+
+	if ((res = snapper_set_rate(sc)) != 0)
+		return res;
+
 	cmd = sc->sc_idmacmd;
 	sc->sc_iintr = intr;
 	sc->sc_iarg = arg;
@@ -1343,41 +1365,43 @@
  */
 
 int
-snapper_set_rate(struct snapper_softc *sc, u_int rate)
+snapper_set_rate(struct snapper_softc *sc)
 {
-	u_int reg;
+	int rate = sc->sc_rate;
+	u_int reg = 0, x;
 	int MCLK;
 	int clksrc, mdiv, sdiv;
 	int mclk_fs;
+	int timo;
 
-	reg = 0;
 	switch (rate) {
 	case 8000:
-		clksrc = 18432000;		/* 18MHz */
+		clksrc = 18432000;                /* 18MHz */
 		reg = CLKSRC_18MHz;
 		mclk_fs = 256;
 		break;
 
 	case 44100:
-		clksrc = 45158400;		/* 45MHz */
+		clksrc = 45158400;                /* 45MHz */
 		reg = CLKSRC_45MHz;
 		mclk_fs = 256;
 		break;
 
+	case 32000:
 	case 48000:
-		clksrc = 49152000;		/* 49MHz */
+		clksrc = 49152000;                /* 49MHz */
 		reg = CLKSRC_49MHz;
 		mclk_fs = 256;
 		break;
 
 	default:
-		DPRINTF("snapper_set_rate: invalid rate %u\n", rate);
+		DPRINTF("snapper_set_rate: invalid rate %d\n", rate);
 		return EINVAL;
 	}
 
 	MCLK = rate * mclk_fs;
-	mdiv = clksrc / MCLK;			/* 4 */
-	sdiv = mclk_fs / 64;			/* 4 */
+	mdiv = clksrc / MCLK; /* 4 */
+	sdiv = mclk_fs / 64; /* 4 */
 
 	switch (mdiv) {
 	case 1:
@@ -1406,19 +1430,38 @@
 		break;
 	}
 
-	reg |= SCLK_MASTER;	/* XXX master mode */
+	reg |= SCLK_MASTER;        /* XXX master mode */
 
 	reg |= SERIAL_64x;
 
+	x = in32rb(sc->sc_reg + I2S_FORMAT);
+	if (x == reg)
+	        return 0;        /* No change; do nothing. */
+
 	/* stereo input and output */
-	DPRINTF("I2SSetDataWordSizeReg 0x%08x -> 0x%08x\n",
-	    in32rb(sc->sc_reg + I2S_WORDSIZE), 0x02000200);
 	out32rb(sc->sc_reg + I2S_WORDSIZE, 0x02000200);
 
-	DPRINTF("I2SSetSerialFormatReg 0x%x -> 0x%x\n",
-	    in32rb(sc->sc_reg + I2S_FORMAT), reg);
+	/* Clear CLKSTOPPEND. */
+	out32rb(sc->sc_reg + I2S_INT, I2S_INT_CLKSTOPPEND);
+
+	x = in32rb(0x8000003c);                /* FCR */
+	x &= ~I2S0CLKEN;                /* XXX I2S0 */
+	out32rb(0x8000003c, x);
+
+	/* Wait until clock is stopped. */
+	for (timo = 1000; timo > 0; timo--) {
+		if (in32rb(sc->sc_reg + I2S_INT) & I2S_INT_CLKSTOPPEND)
+			goto done;
+		delay(1);
+	}
+	printf("snapper_set_rate: timeout\n");
+done:
 	out32rb(sc->sc_reg + I2S_FORMAT, reg);
 
+	x = in32rb(0x8000003c);
+	x |= I2S0CLKEN;
+	out32rb(0x8000003c, x);
+
 	return 0;
 }
 
@@ -1666,14 +1709,6 @@
 	return -1;
 }
 
-/* FCR(0x3c) bits */
-#define I2S0CLKEN	0x1000
-#define I2S0EN		0x2000
-#define I2S1CLKEN	0x080000
-#define I2S1EN		0x100000
-
-#define FCR3C_BITMASK "\020\25I2S1EN\24I2S1CLKEN\16I2S0EN\15I2S0CLKEN"
-
 void
 snapper_init(struct snapper_softc *sc, int node)
 {
@@ -1734,8 +1769,7 @@
 		intr_establish(headphone_detect_intr, IST_EDGE, IPL_AUDIO,
 		    snapper_cint, sc);
 
-	/* "sample-rates" (44100, 48000) */
-	snapper_set_rate(sc, 44100);
+	sc->sc_rate = 44100; /* default rate */
 
 	/* Enable headphone interrupt? */
 	*headphone_detect |= 0x80;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ CUT HERE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^