Subject: Re: Turtle Beach Santa Cruz sound card
To: =?iso-8859-1?Q?St=E9phane?= Witzmann <stephane.witzmann@gmail.com>
From: Lubomir Sedlacik <salo@Xtrmntr.org>
List: netbsd-users
Date: 10/27/2005 12:09:45
--NT59pYSnj1ZLVgEN
Content-Type: multipart/mixed; boundary="lBqJz4CGKwlWe7/k"
Content-Disposition: inline


--lBqJz4CGKwlWe7/k
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Thu, Oct 27, 2005 at 05:38:54AM +0200, St=E9phane Witzmann wrote:
> A few builds and reboots later... I was wrong again. What makes it work is
> just *luck*. The best (and only) fix I have found so far : reboot until it
> works.

the clcs(4) driver is notoriously known to have issues with
initialization (among other things..).

there is a patch from yasufumi itoh flying around which still doesn't
solve all the issues clcs(4) had but could help with the initialization
problems.  i am attaching an old copy i used with T22 months ago.

regards,

--=20
-- Lubomir Sedlacik <salo@{NetBSD,Xtrmntr,silcnet}.org>   --

--lBqJz4CGKwlWe7/k
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="clcs-20040414.diff"
Content-Transfer-Encoding: quoted-printable

diff -uF^[a-zA-Z_][a-z 	A-Z0-9_]*(.*[^;]$ cs4280.c.orig cs4280.c
--- cs4280.c.orig	Tue May  6 09:57:51 2003
+++ cs4280.c	Wed Apr 14 22:52:10 2004
@@ -92,6 +92,8 @@ __KERNEL_RCSID(0, "$NetBSD: cs4280.c,v 1
 int  cs4280_match(struct device *, struct cfdata *, void *);
 void cs4280_attach(struct device *, struct device *, void *);
 int  cs4280_intr(void *);
+int  cs4280_open(void *, int);
+void cs4280_close(void *);
 int  cs4280_query_encoding(void *, struct audio_encoding *);
 int  cs4280_set_params(void *, int, int, struct audio_params *, struct aud=
io_params *);
 int  cs4280_halt_output(void *);
@@ -102,6 +104,8 @@ int  cs4280_trigger_output(void *, void=20
 int  cs4280_trigger_input(void *, void *, void *, int, void (*)(void *),
                           void *, struct audio_params *);
=20
+static int cs4280_reinit(struct cs428x_softc *, int);
+static void cs4280_reset_codec1(struct cs428x_softc *, int);
 void cs4280_reset_codec(void *);
=20
 /* For PowerHook */
@@ -110,11 +114,12 @@ void cs4280_power(int, void *);
 /* Internal functions */
 void cs4280_set_adc_rate(struct cs428x_softc *, int );
 void cs4280_set_dac_rate(struct cs428x_softc *, int );
+void cs4280_clear_image(struct cs428x_softc *);
 int  cs4280_download(struct cs428x_softc *, const u_int32_t *, u_int32_t, =
u_int32_t);
 int  cs4280_download_image(struct cs428x_softc *);
 void cs4280_reset(void *);
 int  cs4280_get_portnum_by_name(struct cs428x_softc *, char *, char *, cha=
r *);
-int  cs4280_init(struct cs428x_softc *, int);
+int  cs4280_init(struct cs428x_softc *, int, int);
 void cs4280_clear_fifos(struct cs428x_softc *);
=20
 #if CS4280_DEBUG > 10
@@ -124,8 +129,8 @@ int  cs4280_checkimage(struct cs428x_sof
 #endif
=20
 struct audio_hw_if cs4280_hw_if =3D {
-	cs428x_open,
-	cs428x_close,
+	cs4280_open,
+	cs4280_close,
 	NULL,
 	cs4280_query_encoding,
 	cs4280_set_params,
@@ -265,7 +270,13 @@ cs4280_attach(parent, self, aux)
 		mem |=3D 0x00002000;
 		pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, mem);
 	}
-=09
+
+	/* Initialization */
+	if (cs4280_init(sc, 1, 1) !=3D 0 &&
+	    cs4280_init(sc, 1, 1) !=3D 0 &&
+	    cs4280_init(sc, 1, 1))
+		return;
+
 	/* Map and establish the interrupt. */
 	if (pci_intr_map(pa, &ih)) {
 		aprint_error("%s: couldn't map interrupt\n",
@@ -285,10 +296,6 @@ cs4280_attach(parent, self, aux)
 	}
 	aprint_normal("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
=20
-	/* Initialization */
-	if(cs4280_init(sc, 1) !=3D 0)
-		return;
-
 	sc->type =3D TYPE_CS4280;
 	sc->halt_input  =3D cs4280_halt_input;
 	sc->halt_output =3D cs4280_halt_output;
@@ -497,6 +504,68 @@ cs4280_intr(p)
 }
=20
 int
+cs4280_open(void *addr, int flags)
+{
+	struct cs428x_softc *sc =3D addr;
+
+	/* re-init the device, in case it failed on the last close */
+	if (sc->sc_needs_reset || sc->sc_errstat !=3D CS428X_AC97_NOERR)
+		if (cs4280_reinit(sc, 0))
+			return EIO;
+
+	sc->sc_needs_reset =3D 0;
+
+	return 0;
+}
+
+void
+cs4280_close(void *addr)
+{
+	struct cs428x_softc *sc =3D addr;
+
+	if (sc->sc_needs_reset) {
+		if (cs4280_reinit(sc, 0))
+			return;		/* failed */
+		sc->sc_needs_reset =3D 0;
+	}
+}
+
+static int
+cs4280_reinit(struct cs428x_softc *sc, int nowait)
+{
+	int i;
+
+	/*
+	 * XXX this is probably overkill
+	 *
+	 * Playback/recording will sometimes become noisy
+	 * when used repeatedly.  I don't know how to avoid
+	 * this, but resetting the chip should bring it to
+	 * a sane state.
+	 */
+	for (i =3D 0; i < 5; i++) {
+		sc->sc_errstat =3D CS428X_AC97_ABORT_ON_ERROR;
+
+		/* perform reset */
+		if (cs4280_init(sc, 0, nowait) !=3D 0)
+			continue;
+		cs4280_reset_codec1(sc, nowait);
+
+		/* restore ac97 registers */
+		(*sc->codec_if->vtbl->restore_ports)(sc->codec_if);
+
+		if (sc->sc_errstat =3D=3D CS428X_AC97_ABORT_ON_ERROR) {
+			/* reninit complete */
+			sc->sc_errstat =3D CS428X_AC97_NOERR;
+			return 0;
+		}
+	}
+
+	printf("%s: cs4280_reinit: reinit failed\n", sc->sc_dev.dv_xname);
+	return 1;
+}
+
+int
 cs4280_query_encoding(addr, fp)
 	void *addr;
 	struct audio_encoding *fp;
@@ -667,7 +736,15 @@ cs4280_halt_output(addr)
 =09
 	mem =3D BA1READ4(sc, CS4280_PCTL);
 	BA1WRITE4(sc, CS4280_PCTL, mem & ~PCTL_MASK);
+#if 1
+	BA1WRITE4(sc, CS4280_SPCR, SPCR_RSTSP);
+	delay(100);
+	BA1WRITE4(sc, CS4280_SPCR, 0);
+	BA1WRITE4(sc, CS4280_SPCR, SPCR_RUN | SPCR_RUNFR);
+	delay(100);
+#endif
 	sc->sc_prun =3D 0;
+	sc->sc_needs_reset =3D 1;
 	return 0;
 }
=20
@@ -680,7 +757,15 @@ cs4280_halt_input(addr)
=20
 	mem =3D BA1READ4(sc, CS4280_CCTL);
 	BA1WRITE4(sc, CS4280_CCTL, mem & ~CCTL_MASK);
+#if 1
+	BA1WRITE4(sc, CS4280_SPCR, SPCR_RSTSP);
+	delay(100);
+	BA1WRITE4(sc, CS4280_SPCR, 0);
+	BA1WRITE4(sc, CS4280_SPCR, SPCR_RUN | SPCR_RUNFR);
+	delay(100);
+#endif
 	sc->sc_rrun =3D 0;
+	sc->sc_needs_reset =3D 1;
 	return 0;
 }
=20
@@ -780,6 +865,9 @@ cs4280_trigger_output(addr, start, end,=20
 	sc->sc_prate =3D param->sample_rate;
 	cs4280_set_dac_rate(sc, param->sample_rate);
=20
+#if 1
+	BA1WRITE4(sc, CS4280_SPCR, BA1READ4(sc, CS4280_SPCR) | SPCR_DRQEN);
+#endif
 	pctl =3D BA1READ4(sc, CS4280_PCTL) & ~PCTL_MASK;
 	pctl |=3D sc->pctl;
 	BA1WRITE4(sc, CS4280_PCTL, pctl);
@@ -854,6 +942,9 @@ cs4280_trigger_input(addr, start, end, b
 	sc->sc_rrate =3D param->sample_rate;
 	cs4280_set_adc_rate(sc, param->sample_rate);
=20
+#if 1
+	BA1WRITE4(sc, CS4280_SPCR, BA1READ4(sc, CS4280_SPCR) | SPCR_DRQEN);
+#endif
 	cctl =3D BA1READ4(sc, CS4280_CCTL) & ~CCTL_MASK;
 	cctl |=3D sc->cctl;
 	BA1WRITE4(sc, CS4280_CCTL, cctl);
@@ -907,11 +998,7 @@ cs4280_power(why, v)
 			return;
 		}
 		sc->sc_suspend =3D why;
-		cs4280_init(sc, 0);
-		cs4280_reset_codec(sc);
-
-		/* restore ac97 registers */
-		(*sc->codec_if->vtbl->restore_ports)(sc->codec_if);
+		cs4280_reinit(sc, 1);
=20
 		/* restore DMA related status */
 		if(sc->sc_prun) {
@@ -942,13 +1029,10 @@ cs4280_power(why, v)
=20
 /* control AC97 codec */
 void
-cs4280_reset_codec(void *addr)
+cs4280_reset_codec1(struct cs428x_softc *sc, int nosleep)
 {
-	struct cs428x_softc *sc;
 	int n;
=20
-	sc =3D addr;
-
 	/* Reset codec */
 	BA0WRITE4(sc, CS428X_ACCTL, 0);
 	delay(100);    /* delay 100us */
@@ -960,21 +1044,37 @@ cs4280_reset_codec(void *addr)
=20
 	/* Enable AC-link sync generation */
 	BA0WRITE4(sc, CS428X_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
-	delay(50*1000); /* XXX delay 50ms */
-=09
+	if (nosleep)
+		delay(50*1000); /* XXX delay 50ms */
+	else
+		tsleep(sc, PWAIT, "clcsa1", hz / 10);
+
 	/* Assert valid frame signal */
 	BA0WRITE4(sc, CS428X_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
=20
 	/* Wait for valid AC97 input slot */
 	n =3D 0;
-	while ((BA0READ4(sc, CS428X_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) !=3D
-	       (ACISV_ISV3 | ACISV_ISV4)) {
-		delay(1000);
+	do {
 		if (++n > 1000) {
 			printf("reset_codec: AC97 inputs slot ready timeout\n");
 			return;
 		}
-	}
+		if (nosleep)
+			delay(1000);
+		else
+			tsleep(sc, PWAIT, "clcsa2", hz / 4);
+	} while ((BA0READ4(sc, CS428X_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) !=3D
+	       (ACISV_ISV3 | ACISV_ISV4));
+#if CS4280_DEBUG > 1
+	if (n) printf("%s: cs4280_reset_codec: n %d\n", sc->sc_dev.dv_xname, n);
+#endif
+}
+
+void
+cs4280_reset_codec(void *addr)
+{
+
+	cs4280_reset_codec1(addr, 1);
 }
=20
=20
@@ -1147,6 +1247,16 @@ cs4280_set_dac_rate(sc, rate)
 	BA1WRITE4(sc, CS4280_PPI, ppi);
 }
=20
+void
+cs4280_clear_image(sc)
+	struct cs428x_softc *sc;
+{
+	u_int32_t adr;
+
+	for (adr =3D 0; adr < CS4280_BA1_SIZE; adr +=3D sizeof(u_int32_t))
+		BA1WRITE4(sc, adr, 0);
+}
+
 /* Download Proceessor Code and Data image */
 int
 cs4280_download(sc, src, offset, len)
@@ -1194,6 +1304,8 @@ cs4280_download_image(sc)
 	int idx, err;
 	u_int32_t offset =3D 0;
=20
+	cs4280_clear_image(sc);
+
 	err =3D 0;
 	for (idx =3D 0; idx < BA1_MEMORY_COUNT; ++idx) {
 		err =3D cs4280_download(sc, &BA1Struct.map[offset],
@@ -1235,9 +1347,9 @@ cs4280_get_portnum_by_name(sc, class, de
 }
=20
 int
-cs4280_init(sc, init)
+cs4280_init(sc, init, nosleep)
 	struct cs428x_softc *sc;
-	int init;
+	int init, nosleep;
 {
 	int n;
 	u_int32_t mem;
@@ -1278,8 +1390,12 @@ cs4280_init(sc, init)
 	delay(50*1000); /* delay 50ms */
 =09
 	/* Turn on clock */
+#if 0
 	mem =3D BA0READ4(sc, CS4280_CLKCR1) | CLKCR1_SWCE;
 	BA0WRITE4(sc, CS4280_CLKCR1, mem);
+#else
+	BA0WRITE4(sc, CS4280_CLKCR1, CLKCR1_PLLP|CLKCR1_SWCE);
+#endif
 =09
 	/* Set the serial port FIFO pointer to the
 	 * first sample in FIFO. (not documented) */
@@ -1297,29 +1413,41 @@ cs4280_init(sc, init)
 =09
 	/* Wait for CODEC ready */
 	n =3D 0;
-	while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_CRDY) =3D=3D 0) {
-		delay(125);
+	do {
 		if (++n > 1000) {
 			printf("%s: codec ready timeout\n",
 			       sc->sc_dev.dv_xname);
 			return(1);
 		}
-	}
+		if (nosleep)
+			delay(125);
+		else
+			tsleep(sc, PWAIT, "clcs1", 2);
+	} while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_CRDY) =3D=3D 0);
+#if CS4280_DEBUG > 1
+	if (n > 1) printf("%s: cs4280_init: n1 %d\n", sc->sc_dev.dv_xname, n-1);
+#endif
=20
 	/* Assert valid frame signal */
 	BA0WRITE4(sc, CS428X_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
=20
 	/* Wait for valid AC97 input slot */
 	n =3D 0;
-	while ((BA0READ4(sc, CS428X_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) !=3D
-	       (ACISV_ISV3 | ACISV_ISV4)) {
-		delay(1000);
+	do {
 		if (++n > 1000) {
 			printf("AC97 inputs slot ready timeout\n");
 			return(1);
 		}
-	}
-=09
+		if (nosleep)
+			delay(2000);
+		else
+			tsleep(sc, PWAIT, "clcs2", hz / 10);
+	} while ((BA0READ4(sc, CS428X_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) !=3D
+	       (ACISV_ISV3 | ACISV_ISV4));
+#if CS4280_DEBUG > 1
+	if (n > 1) printf("%s: cs4280_init: n2 %d\n", sc->sc_dev.dv_xname, n-1);
+#endif
+
 	/* Set AC97 output slot valid signals */
 	BA0WRITE4(sc, CS428X_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
=20
@@ -1358,22 +1486,28 @@ cs4280_init(sc, init)
=20
 	/* Monitor RUNFR bit in SPCR for 1 to 0 transition */
 	n =3D 0;
-	while (BA1READ4(sc, CS4280_SPCR) & SPCR_RUNFR) {
-		delay(10);
+	do {
 		if (++n > 1000) {
 			printf("SPCR 1->0 transition timeout\n");
 			return(1);
 		}
-	}
-=09
-	n =3D 0;
-	while (!(BA1READ4(sc, CS4280_SPCS) & SPCS_SPRUN)) {
 		delay(10);
+	} while (BA1READ4(sc, CS4280_SPCR) & SPCR_RUNFR);
+#ifdef CS4280_DEBUG
+	if (n > 1) printf("%s: cs4280_init: n3 %d\n", sc->sc_dev.dv_xname, n-1);
+#endif
+
+	n =3D 0;
+	do {
 		if (++n > 1000) {
 			printf("SPCS 0->1 transition timeout\n");
 			return(1);
 		}
-	}
+		delay(10);
+	} while (!(BA1READ4(sc, CS4280_SPCS) & SPCS_SPRUN));
+#ifdef CS4280_DEBUG
+	if (n > 1) printf("%s: cs4280_init: n4 %d\n", sc->sc_dev.dv_xname, n-1);
+#endif
 	/* Processor is now running !!! */
=20
 	/* Setup  volume */
@@ -1431,6 +1565,11 @@ cs4280_clear_fifos(sc)
 				break;
 			}
 		}
+#ifdef CS4280_DEBUG
+		if (n)
+			printf("%s: clear_fifo: n %d (cnt %d)\n",
+			    sc->sc_dev.dv_xname, n, cnt);
+#endif
 		BA0WRITE4(sc, CS4280_SERBAD, cnt);
 		BA0WRITE4(sc, CS4280_SERBCM, SERBCM_WRC);
 	}
diff -u cs428x.h.orig cs428x.h
--- cs428x.h.orig	Tue May  6 09:57:51 2003
+++ cs428x.h	Wed Apr 14 22:52:10 2004
@@ -118,10 +118,20 @@
=20
 	/*
 	 * XXX
-	 * Actually thease 2 variables are needed only for CS4280.
+	 * Actually thease 4 variables are needed only for CS4280.
 	 */
 	u_int32_t pctl;
 	u_int32_t cctl;
+	int	sc_needs_reset;
+	int	sc_needs_pciclk;
+
+	/* another XXX */
+	enum {
+		CS428X_AC97_NOERR,
+		CS428X_AC97_HAD_ERR,
+		CS428X_AC97_ABORT_ON_ERROR,
+		CS428X_AC97_ABORTED
+	} sc_errstat;
=20
 	/* AC97 CODEC */
 	struct ac97_codec_if *codec_if;
diff -uF^[a-zA-Z_][a-z 	A-Z0-9_]*(.*[^;]$ cs428x.c.orig cs428x.c
--- cs428x.c.orig	Tue May  6 09:57:51 2003
+++ cs428x.c	Wed Apr 14 22:52:10 2004
@@ -273,7 +273,7 @@ cs428x_read_codec(void *addr, u_int8_t a
 	n =3D 0;
 	while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_VSTS) =3D=3D 0) {
 		delay(1);
-		while (++n > 1000) {
+		if (++n > 1000) {
 			printf("%s: AC97 read fail (VSTS=3D=3D0) for add=3D0x%0x\n",
 			       sc->sc_dev.dv_xname, ac97_addr);
 			return 1;
@@ -302,6 +302,8 @@ cs428x_write_codec(void *addr, u_int8_t=20
 	BA0WRITE4(sc, CS428X_ACCTL, acctl);
=20
 	if (cs428x_src_wait(sc) < 0) {
+		if (sc->sc_errstat =3D=3D CS428X_AC97_ABORTED)
+			return 1;	/* do not print error */
 		printf("%s: AC97 write fail (DCV!=3D0) for add=3D0x%02x data=3D"
 		       "0x%04x\n", sc->sc_dev.dv_xname, ac97_addr, ac97_data);
 		return 1;
@@ -379,10 +381,16 @@ cs428x_src_wait(sc)
=20
 	n =3D 0;
 	while ((BA0READ4(sc, CS428X_ACCTL) & ACCTL_DCV)) {
+		if (sc->sc_errstat =3D=3D CS428X_AC97_ABORTED)
+			return -1;
 		delay(1000);
-		while (++n > 1000) {
+		if (++n > 1000) {
 			printf("cs428x_src_wait: 0x%08x\n",
 			    BA0READ4(sc, CS428X_ACCTL));
+			if (sc->sc_errstat =3D=3D CS428X_AC97_ABORT_ON_ERROR)
+				sc->sc_errstat =3D CS428X_AC97_ABORTED;
+			else
+				sc->sc_errstat =3D CS428X_AC97_HAD_ERR;
 			return -1;
 		}
 	}

--lBqJz4CGKwlWe7/k--

--NT59pYSnj1ZLVgEN
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (NetBSD)

iD8DBQFDYKdoiwjDDlS8cmMRAvoxAJ94SuCulzwusxR+LzPaUP5eSWqkuQCfSWnJ
voKhQc8hnsxGt4X29Ab1GWI=
=BFLh
-----END PGP SIGNATURE-----

--NT59pYSnj1ZLVgEN--