Subject: Re: kern/23584 (page-fault in clcs(4) after unclean reboot)
To: None <jmcneill@netbsd.org, gnats-admin@netbsd.org,>
From: Jared D. McNeill <jmcneill@invisible.ca>
List: netbsd-bugs
Date: 01/02/2006 18:50:02
The following reply was made to PR kern/23584; it has been noted by GNATS.

From: "Jared D. McNeill" <jmcneill@invisible.ca>
To: gnats-bugs@netbsd.org
Cc: jmcneill@netbsd.org, kern-bug-people@netbsd.org,
	netbsd-bugs@netbsd.org, gnats-admin@netbsd.org,
	stephane.witzmann@gmail.com
Subject: Re: kern/23584 (page-fault in clcs(4) after unclean reboot)
Date: Mon, 02 Jan 2006 14:49:36 -0400

 Can you try the following patch Stephane? Sorry, it's mangled with your
 Santa Cruz patch.
 
 Index: cs4280.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/pci/cs4280.c,v
 retrieving revision 1.35
 diff -u -r1.35 cs4280.c
 --- cs4280.c	28 Jun 2005 00:28:41 -0000	1.35
 +++ cs4280.c	2 Jan 2006 18:48:12 -0000
 @@ -110,6 +110,8 @@
  static void cs4280_power(int, void *);
  
  /* Internal functions */
 +static const struct cs4280_card_t * cs4280_identify_card(struct
 pci_attach_args *);
 +static void cs4280_setup_ac97eamp(struct cs428x_softc *);
  static void cs4280_set_adc_rate(struct cs428x_softc *, int );
  static void cs4280_set_dac_rate(struct cs428x_softc *, int );
  static int  cs4280_download(struct cs428x_softc *, const uint32_t *,
 uint32_t,
 @@ -126,6 +128,27 @@
  			      uint32_t);
  #endif
  
 +/* Special cards */
 +struct cs4280_card_t
 +{
 +	pcireg_t id;
 +	enum cs428x_flag flags;
 +};
 +
 +#define _card(vend, prod, flags) \
 +	{PCI_ID_CODE(vend, prod), flags}
 +
 +static const struct cs4280_card_t cs4280_cards[] = {
 +	_card(PCI_VENDOR_MITAC, PCI_PRODUCT_MITAC_MI6020,
 +	      CS428X_FLAG_AC97EAMP),
 +	_card(PCI_VENDOR_TURTLE_BEACH, PCI_PRODUCT_TURTLE_BEACH_SANTA_CRUZ,
 +	      CS428X_FLAG_AC97EAMP)
 +};
 +
 +#undef _card
 +
 +#define CS4280_CARDS_SIZE
 (sizeof(cs4280_cards)/sizeof(cs4280_cards[0]))
 +
  static const struct audio_hw_if cs4280_hw_if = {
  	NULL,			/* open */
  	NULL,			/* close */
 @@ -206,6 +229,7 @@
  	struct cs428x_softc *sc;
  	struct pci_attach_args *pa;
  	pci_chipset_tag_t pc;
 +	const struct cs4280_card_t *cs_card;
  	char const *intrstr;
  	pci_intr_handle_t ih;
  	pcireg_t reg;
 @@ -222,6 +246,16 @@
  	aprint_normal(": %s (rev. 0x%02x)\n", devinfo,
  	    PCI_REVISION(pa->pa_class));
  
 +	cs_card = cs4280_identify_card(pa);
 +	if (cs_card != NULL) {
 +		aprint_normal("%s: %s %s\n",sc->sc_dev.dv_xname,
 +			      pci_findvendor(cs_card->id),
 +			      pci_findproduct(cs_card->id));
 +		sc->sc_flags = cs_card->flags;
 +	} else {
 +		sc->sc_flags = CS428X_FLAG_NONE;
 +	}
 +
  	/* Map I/O register */
  	if (pci_mapreg_map(pa, PCI_BA0,
  	    PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0,
 @@ -309,6 +343,9 @@
  		return;
  	}
  
 +	/* power AC97 external amp if needed */
 +	cs4280_setup_ac97eamp(sc);
 +
  	audio_attach_mi(&cs4280_hw_if, sc, &sc->sc_dev);
  
  #if NMIDI > 0
 @@ -364,18 +401,18 @@
  		if (sc->sc_prun) {
  			if ((sc->sc_pi%sc->sc_pcount) == 0)
  				sc->sc_pintr(sc->sc_parg);
 +			/* copy buffer */
 +			++sc->sc_pi;
 +			empty_dma = sc->sc_pdma->addr;
 +			if (sc->sc_pi&1)
 +				empty_dma += sc->hw_blocksize;
 +			memcpy(empty_dma, sc->sc_pn, sc->hw_blocksize);
 +			sc->sc_pn += sc->hw_blocksize;
 +			if (sc->sc_pn >= sc->sc_pe)
 +				sc->sc_pn = sc->sc_ps;
  		} else {
  			printf("unexpected play intr\n");
  		}
 -		/* copy buffer */
 -		++sc->sc_pi;
 -		empty_dma = sc->sc_pdma->addr;
 -		if (sc->sc_pi&1)
 -			empty_dma += sc->hw_blocksize;
 -		memcpy(empty_dma, sc->sc_pn, sc->hw_blocksize);
 -		sc->sc_pn += sc->hw_blocksize;
 -		if (sc->sc_pn >= sc->sc_pe)
 -			sc->sc_pn = sc->sc_ps;
  		BA1WRITE4(sc, CS4280_PFIE, mem);
  	}
  	/* Capture Interrupt */
 @@ -386,58 +423,62 @@
  		handled = 1;
  		mem = BA1READ4(sc, CS4280_CIE);
  		BA1WRITE4(sc, CS4280_CIE, (mem & ~CIE_CI_MASK) | CIE_CI_DISABLE);
 -		++sc->sc_ri;
 -		empty_dma = sc->sc_rdma->addr;
 -		if ((sc->sc_ri&1) == 0)
 -			empty_dma += sc->hw_blocksize;
 -
 -		/*
 -		 * XXX
 -		 * I think this audio data conversion should be
 -		 * happend in upper layer, but I put this here
 -		 * since there is no conversion function available.
 -		 */
 -		switch(sc->sc_rparam) {
 -		case CF_16BIT_STEREO:
 -			/* just copy it */
 -			memcpy(sc->sc_rn, empty_dma, sc->hw_blocksize);
 -			sc->sc_rn += sc->hw_blocksize;
 -			break;
 -		case CF_16BIT_MONO:
 -			for (i = 0; i < 512; i++) {
 -				rdata  = *((int16_t *)empty_dma)>>1;
 -				empty_dma += 2;
 -				rdata += *((int16_t *)empty_dma)>>1;
 -				empty_dma += 2;
 -				*((int16_t *)sc->sc_rn) = rdata;
 -				sc->sc_rn += 2;
 -			}
 -			break;
 -		case CF_8BIT_STEREO:
 -			for (i = 0; i < 512; i++) {
 -				rdata = *((int16_t*)empty_dma);
 -				empty_dma += 2;
 -				*sc->sc_rn++ = rdata >> 8;
 -				rdata = *((int16_t*)empty_dma);
 -				empty_dma += 2;
 -				*sc->sc_rn++ = rdata >> 8;
 -			}
 -			break;
 -		case CF_8BIT_MONO:
 -			for (i = 0; i < 512; i++) {
 -				rdata =	 *((int16_t*)empty_dma) >>1;
 -				empty_dma += 2;
 -				rdata += *((int16_t*)empty_dma) >>1;
 -				empty_dma += 2;
 -				*sc->sc_rn++ = rdata >>8;
 +
 +		if (sc->sc_rrun) {
 +			++sc->sc_ri;
 +			empty_dma = sc->sc_rdma->addr;
 +			if ((sc->sc_ri&1) == 0)
 +				empty_dma += sc->hw_blocksize;
 +
 +			/*
 +			 * XXX
 +			 * I think this audio data conversion should be
 +			 * happend in upper layer, but I put this here
 +			 * since there is no conversion function available.
 +			 */
 +			switch(sc->sc_rparam) {
 +			case CF_16BIT_STEREO:
 +				/* just copy it */
 +				memcpy(sc->sc_rn, empty_dma, sc->hw_blocksize);
 +				sc->sc_rn += sc->hw_blocksize;
 +				break;
 +			case CF_16BIT_MONO:
 +				for (i = 0; i < 512; i++) {
 +					rdata  = *((int16_t *)empty_dma)>>1;
 +					empty_dma += 2;
 +					rdata += *((int16_t *)empty_dma)>>1;
 +					empty_dma += 2;
 +					*((int16_t *)sc->sc_rn) = rdata;
 +					sc->sc_rn += 2;
 +				}
 +				break;
 +			case CF_8BIT_STEREO:
 +				for (i = 0; i < 512; i++) {
 +					rdata = *((int16_t*)empty_dma);
 +					empty_dma += 2;
 +					*sc->sc_rn++ = rdata >> 8;
 +					rdata = *((int16_t*)empty_dma);
 +					empty_dma += 2;
 +					*sc->sc_rn++ = rdata >> 8;
 +				}
 +				break;
 +			case CF_8BIT_MONO:
 +				for (i = 0; i < 512; i++) {
 +					rdata =	 *((int16_t*)empty_dma) >>1;
 +					empty_dma += 2;
 +					rdata += *((int16_t*)empty_dma) >>1;
 +					empty_dma += 2;
 +					*sc->sc_rn++ = rdata >>8;
 +				}
 +				break;
 +			default:
 +				/* Should not reach here */
 +				printf("unknown sc->sc_rparam: %d\n",
 +				    sc->sc_rparam);
  			}
 -			break;
 -		default:
 -			/* Should not reach here */
 -			printf("unknown sc->sc_rparam: %d\n", sc->sc_rparam);
 +			if (sc->sc_rn >= sc->sc_re)
 +				sc->sc_rn = sc->sc_rs;
  		}
 -		if (sc->sc_rn >= sc->sc_re)
 -			sc->sc_rn = sc->sc_rs;
  		BA1WRITE4(sc, CS4280_CIE, mem);
  		if (sc->sc_rrun) {
  			if ((sc->sc_ri%(sc->sc_rcount)) == 0)
 @@ -957,11 +998,37 @@
  			return ETIMEDOUT;
  		}
  	}
 +
  	return 0;
  }
  
  /* Internal functions */
  
 +static const struct cs4280_card_t *
 +cs4280_identify_card(struct pci_attach_args *pa)
 +{
 +	pcireg_t idreg;
 +	u_int16_t i;
 +
 +	idreg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
 +	for (i = 0; i < CS4280_CARDS_SIZE; i++) {
 +		if (idreg == cs4280_cards[i].id)
 +			return &cs4280_cards[i];
 +	}
 +
 +	return NULL;
 +}
 +
 +static void cs4280_setup_ac97eamp(struct cs428x_softc *sc)
 +{
 +	uint16_t val;
 +
 +	if (sc->sc_flags & CS428X_FLAG_AC97EAMP) {
 +		cs428x_read_codec(sc, AC97_REG_POWER, &val);
 +		cs428x_write_codec(sc, AC97_REG_POWER, val | AC97_POWER_EAMP);
 +	}
 +}
 +
  static void
  cs4280_set_adc_rate(struct cs428x_softc *sc, int rate)
  {
 @@ -1125,7 +1192,7 @@
  	BA1WRITE4(sc, CS4280_PPI, ppi);
  }
  
 -/* Download Proceessor Code and Data image */
 +/* Download Processor Code and Data image */
  static int
  cs4280_download(struct cs428x_softc *sc, const uint32_t *src,
  		uint32_t offset, uint32_t len)
 @@ -1244,6 +1311,9 @@
  	mem = BA0READ4(sc, CS4280_CLKCR1) | CLKCR1_SWCE;
  	BA0WRITE4(sc, CS4280_CLKCR1, mem);
  
 +	/* Enable FIFO Host Bypass */
 +	BA0WRITE4(sc, CS4280_SERBCF, SERBCF_HBP);
 +
  	/* Set the serial port FIFO pointer to the
  	 * first sample in FIFO. (not documented) */
  	cs4280_clear_fifos(sc);