xref: /netbsd-src/sys/arch/amiga/dev/repulse.c (revision 7bf69543cc44690b0976b9f2cf1f540335eb3229)
1 /*	$NetBSD: repulse.c,v 1.24 2022/02/12 23:30:30 andvar Exp $ */
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Ignatios Souvatzis.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: repulse.c,v 1.24 2022/02/12 23:30:30 andvar Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/device.h>
40 #include <sys/fcntl.h>		/* FREAD */
41 #include <sys/bus.h>
42 
43 #include <sys/audioio.h>
44 #include <dev/audio/audio_if.h>
45 
46 #include <dev/ic/ac97reg.h>
47 #include <dev/ic/ac97var.h>
48 
49 #include <amiga/dev/zbusvar.h>
50 #include <amiga/amiga/isr.h>
51 
52 #include <amiga/dev/repulse_firmware.h>
53 
54 #ifndef vu_int8_t
55 #define vu_int8_t volatile uint8_t
56 #endif
57 #ifndef vu_int16_t
58 #define vu_int16_t volatile uint16_t
59 #endif
60 #ifndef vu_int32_t
61 #define vu_int32_t volatile uint32_t
62 #endif
63 
64 /* ac97 attachment functions */
65 
66 int repac_attach(void *, struct ac97_codec_if *);
67 int repac_read(void *, uint8_t, uint16_t *);
68 int repac_write(void *, uint8_t, uint16_t);
69 int repac_reset(void *);
70 enum ac97_host_flag repac_flags(void *);
71 
72 /* audio attachment functions */
73 
74 int rep_getdev(void *, struct audio_device *);
75 int rep_get_props(void *);
76 int rep_halt_output(void *);
77 int rep_halt_input(void *);
78 int rep_query_format(void *, audio_format_query_t *);
79 int rep_set_format(void *, int, const audio_params_t *, const audio_params_t *,
80 	audio_filter_reg_t *, audio_filter_reg_t *);
81 int rep_round_blocksize(void *, int, int, const audio_params_t *);
82 int rep_set_port(void *, mixer_ctrl_t *);
83 int rep_get_port(void *, mixer_ctrl_t *);
84 int rep_query_devinfo(void *, mixer_devinfo_t *);
85 void rep_get_locks(void *, kmutex_t **, kmutex_t **);
86 
87 int rep_start_input(void *, void *, int, void (*)(void *), void *);
88 int rep_start_output(void *, void *, int, void (*)(void *), void *);
89 
90 int rep_intr(void *);
91 
92 
93 /* audio attachment */
94 
95 const struct audio_hw_if rep_hw_if = {
96 	.query_format		= rep_query_format,
97 	.set_format		= rep_set_format,
98 	.round_blocksize	= rep_round_blocksize,
99 	.start_output		= rep_start_output,
100 	.start_input		= rep_start_input,
101 	.halt_output		= rep_halt_output,
102 	.halt_input		= rep_halt_input,
103 	.getdev			= rep_getdev,
104 	.set_port		= rep_set_port,
105 	.get_port		= rep_get_port,
106 	.query_devinfo		= rep_query_devinfo,
107 	.get_props		= rep_get_props,
108 	.get_locks		= rep_get_locks,
109 };
110 
111 /* hardware registers */
112 
113 struct repulse_hw {
114 	vu_int16_t	rhw_status;
115 	vu_int16_t	rhw_fifostatus;		/* 0xrrrrpppp0000flag */
116 	vu_int16_t	rhw_reg_address;
117 	vu_int16_t	rhw_reg_data;
118 /* 0x08 */
119 	vu_int16_t	rhw_fifo_lh;
120 	vu_int16_t	rhw_fifo_ll;
121 	vu_int16_t	rhw_fifo_rh;
122 	vu_int16_t	rhw_fifo_rl;
123 /* 0x10 */
124 	vu_int16_t	rhw_fifo_pack;
125 	vu_int16_t	rhw_play_fifosz;
126 	vu_int32_t	rhw_spdifin_stat;
127 #define	rhw_spdifout_stat rhw_spdifin_stat;
128 
129 /* 0x18 */
130 	vu_int16_t	rhw_capt_fifosz;
131 	vu_int16_t	rhw_version;
132 	vu_int16_t	rhw_dummy1;
133 	vu_int8_t	rhw_firmwareload;
134 /* 0x1F */
135 	vu_int8_t	rhw_dummy2[66 - 31];
136 /* 0x42 */
137 	vu_int16_t	rhw_reset;
138 } /* __attribute__((packed)) */;
139 
140 #define REPSTATUS_PLAY		0x0001
141 #define REPSTATUS_RECORD	0x0002
142 #define REPSTATUS_PLAYFIFORST	0x0004
143 #define REPSTATUS_RECFIFORST	0x0008
144 
145 #define REPSTATUS_REGSENDBUSY	0x0010
146 #define REPSTATUS_LOOPBACK	0x0020
147 #define REPSTATUS_ENSPDIFIN	0x0040
148 #define REPSTATUS_ENSPDIFOUT	0x0080
149 
150 #define REPSTATUS_CODECRESET	0x0200
151 #define REPSTATUS_SPDIFOUT24	0x0400
152 #define REPSTATUS_SPDIFIN24	0x0800
153 
154 #define REPSTATUS_RECIRQENABLE	0x1000
155 #define REPSTATUS_RECIRQACK	0x2000
156 #define REPSTATUS_PLAYIRQENABLE	0x4000
157 #define REPSTATUS_PLAYIRQACK	0x8000
158 
159 #define REPFIFO_PLAYFIFOFULL	0x0001
160 #define REPFIFO_PLAYFIFOEMPTY	0x0002
161 #define REPFIFO_RECFIFOFULL	0x0004
162 #define REPFIFO_RECFIFOEMPTY	0x0008
163 #define REPFIFO_PLAYFIFOGAUGE(x) ((x << 4) & 0xf000)
164 #define REPFIFO_RECFIFOGAUGE(x)		(x & 0xf000)
165 
166 /* AmigaDOS Delay() ticks */
167 
168 #define USECPERTICK	(1000000/50)
169 
170 /* NetBSD device attachment */
171 
172 struct repulse_softc {
173 	device_t		sc_dev;
174 	struct isr		sc_isr;
175 	struct ac97_host_if	sc_achost;
176 	struct ac97_codec_if	*sc_codec_if;
177 
178 	struct repulse_hw	*sc_boardp;
179 
180 	void	(*sc_captmore)(void *);
181 	void	 *sc_captarg;
182 
183 	void	 *sc_captbuf;
184 	int	  sc_captscale;
185 	int	  sc_captbufsz;
186 	unsigned  sc_captflags;
187 
188 
189 	void	(*sc_playmore)(void *);
190 	void	 *sc_playarg;
191 	int	  sc_playscale;
192 	unsigned  sc_playflags;
193 
194 	kmutex_t  sc_lock;
195 	kmutex_t  sc_intr_lock;
196 };
197 
198 const struct audio_format repulse_format = {
199 	.mode		= AUMODE_PLAY | AUMODE_RECORD,
200 	.encoding	= AUDIO_ENCODING_SLINEAR_BE,
201 	.validbits	= 16,
202 	.precision	= 16,
203 	.channels	= 2,
204 	.channel_mask	= AUFMT_STEREO,
205 	.frequency_type	= 6,
206 	.frequency	= { 8000, 16000, 22050, 32000, 44100, 48000 },
207 };
208 
209 int repulse_match (device_t, cfdata_t, void *);
210 void repulse_attach (device_t, device_t, void *);
211 
212 CFATTACH_DECL_NEW(repulse, sizeof(struct repulse_softc),
213     repulse_match, repulse_attach, NULL, NULL);
214 
215 int
repulse_match(device_t parent,cfdata_t cf,void * aux)216 repulse_match(device_t parent, cfdata_t cf, void *aux)
217 {
218 	struct zbus_args *zap;
219 
220 	zap = aux;
221 
222 	if (zap->manid != 0x4144)
223 		return (0);
224 
225 	if (zap->prodid != 0)
226 		return (0);
227 
228 	return (1);
229 }
230 
231 void
repulse_attach(device_t parent,device_t self,void * aux)232 repulse_attach(device_t parent, device_t self, void *aux)
233 {
234 	struct repulse_softc *sc;
235 	struct zbus_args *zap;
236 	struct repulse_hw *bp;
237 	const uint8_t *fwp;
238 	int needs_firmware;
239 	uint16_t a;
240 
241 	sc = device_private(self);
242 	sc->sc_dev = self;
243 	zap = aux;
244 	bp = (struct repulse_hw *)zap->va;
245 	sc->sc_boardp = bp;
246 	sc->sc_playscale = 4;
247 	sc->sc_captscale = 4;
248 
249 	needs_firmware = 0;
250 	if (bp->rhw_fifostatus & 0x00f0)
251 		needs_firmware = 1;
252 	else {
253 		bp->rhw_status = 0x000c;
254 		if (bp->rhw_status != 0 || bp->rhw_fifostatus != 0x0f0a)
255 			needs_firmware = 1;
256 	}
257 
258 	printf(": ");
259 	if (needs_firmware) {
260 		printf("loading ");
261 		bp->rhw_reset = 0;
262 
263 		delay(1 * USECPERTICK);
264 
265 		for (fwp = (const uint8_t *)repulse_firmware;
266 		    fwp < (repulse_firmware_size +
267 		    (const uint8_t *)repulse_firmware); fwp++)
268 			bp->rhw_firmwareload = *fwp;
269 
270 		delay(1 * USECPERTICK);
271 
272 		if (bp->rhw_fifostatus & 0x00f0)
273 			goto Initerr;
274 
275 		a = /* bp->rhw_status;
276 		a |= */ REPSTATUS_CODECRESET;
277 		bp->rhw_status = a;
278 
279 		a = bp->rhw_status;
280 		if ((a & REPSTATUS_CODECRESET) == 0)
281 			goto Initerr;
282 
283 		(void)bp->rhw_status;
284 		(void)bp->rhw_status;
285 		(void)bp->rhw_status;
286 		a = bp->rhw_status;
287 		a &= ~REPSTATUS_CODECRESET;
288 		bp->rhw_status = a;
289 	}
290 
291 	printf("firmware version 0x%x\n", bp->rhw_version);
292 
293 	sc->sc_achost.arg = sc;
294 
295 	sc->sc_achost.reset = repac_reset;
296 	sc->sc_achost.read = repac_read;
297 	sc->sc_achost.write = repac_write;
298 	sc->sc_achost.attach = repac_attach;
299 	sc->sc_achost.flags = 0;
300 
301 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
302 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
303 
304 	if (ac97_attach(&sc->sc_achost, self, &sc->sc_lock)) {
305 		printf("%s: error attaching codec\n", device_xname(self));
306 		return;
307 	}
308 
309 #ifdef DIAGNOSTIC
310 	/*
311 	 * Print a warning if the codec doesn't support hardware variable
312 	 * rate audio. As the initial incarnations of the Repulse board
313 	 * are AC'97 2.1, it is expected that we'll always have VRA.
314 	 */
315 	/*
316 	 * XXX this should be a panic(). OTOH, audio codec speed is not
317 	 * important enough to do this.
318 	 */
319 	a = sc->sc_codec_if->vtbl->get_extcaps(sc->sc_codec_if);
320 	if (!(a & AC97_EXT_AUDIO_VRA)) {
321 		printf("%s: warning: codec doesn't support "
322 		    "hardware AC'97 2.0 Variable Rate Audio\n",
323 			device_xname(self));
324 	}
325 #endif
326 
327 	sc->sc_isr.isr_ipl = 2;
328 	sc->sc_isr.isr_arg = sc;
329 	sc->sc_isr.isr_intr = rep_intr;
330 	add_isr(&sc->sc_isr);
331 
332 	audio_attach_mi(&rep_hw_if, sc, self);
333 
334 	return;
335 
336 Initerr:
337 	printf("\n%s: firmware not successfully loaded\n", device_xname(self));
338 	return;
339 
340 }
341 
342 int
repac_reset(void * arg)343 repac_reset(void *arg)
344 {
345 	struct repulse_softc *sc;
346 	struct repulse_hw *bp;
347 	uint16_t a;
348 
349 	sc = arg;
350 	bp = sc->sc_boardp;
351 	a = bp->rhw_status;
352 	a |= REPSTATUS_CODECRESET;
353 	bp->rhw_status = a;
354 
355 	a = bp->rhw_status;
356 #ifdef DIAGNOSTIC
357 	if ((a & REPSTATUS_CODECRESET) == 0)
358 		panic("%s: cannot set reset bit", device_xname(sc->sc_dev));
359 #endif
360 
361 	a = bp->rhw_status;
362 	a = bp->rhw_status;
363 	a = bp->rhw_status;
364 	a = bp->rhw_status;
365 	a &= ~REPSTATUS_CODECRESET;
366 	bp->rhw_status = a;
367 	return 0;
368 }
369 
370 int
repac_read(void * arg,u_int8_t reg,u_int16_t * valuep)371 repac_read(void *arg, u_int8_t reg, u_int16_t *valuep)
372 {
373 	struct repulse_softc *sc;
374 	struct repulse_hw *bp;
375 
376 	sc = arg;
377 	bp = sc->sc_boardp;
378 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY)
379 		continue;
380 	bp->rhw_reg_address = (reg & 0x7F) | 0x80;
381 
382 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY)
383 		continue;
384 
385 	*valuep = bp->rhw_reg_data;
386 
387 	return 0;
388 }
389 
390 int
repac_write(void * arg,uint8_t reg,uint16_t value)391 repac_write(void *arg, uint8_t reg, uint16_t value)
392 {
393 	struct repulse_softc *sc;
394 	struct repulse_hw *bp;
395 
396 	sc = arg;
397 	bp = sc->sc_boardp;
398 	bp->rhw_reg_data = value;
399 	bp->rhw_reg_address = reg & 0x7F;
400 
401 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY)
402 		continue;
403 
404 	return 0;
405 }
406 
407 int
repac_attach(void * arg,struct ac97_codec_if * acip)408 repac_attach(void *arg, struct ac97_codec_if *acip)
409 {
410 	struct repulse_softc *sc;
411 
412 	sc = arg;
413 	sc->sc_codec_if = acip;
414 
415 	return 0;
416 }
417 
418 /* audio(9) support stuff which is not ac97-constant */
419 
420 int
rep_getdev(void * arg,struct audio_device * retp)421 rep_getdev(void *arg, struct audio_device *retp)
422 {
423 	struct repulse_softc *sc;
424 	struct repulse_hw *bp;
425 
426 	if (retp != NULL) {
427 		sc = arg;
428 		bp = sc->sc_boardp;
429 		strncpy(retp->name, "Repulse", sizeof(retp->name));
430 		snprintf(retp->version, sizeof(retp->version), "0x%x",
431 			bp->rhw_version);
432 		strncpy(retp->config, "", sizeof(retp->config));
433 	}
434 
435 	return 0;
436 }
437 
438 int
rep_get_props(void * v)439 rep_get_props(void *v)
440 {
441 
442 	return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
443 	    AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
444 }
445 
446 int
rep_halt_output(void * arg)447 rep_halt_output(void *arg)
448 {
449 	struct repulse_softc *sc;
450 	struct repulse_hw *bp;
451 
452 	sc = arg;
453 	bp = sc->sc_boardp;
454 	bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY);
455 
456 
457 	return 0;
458 }
459 
460 int
rep_halt_input(void * arg)461 rep_halt_input(void *arg)
462 {
463 	struct repulse_softc *sc;
464 	struct repulse_hw *bp;
465 
466 	sc = arg;
467 	bp = sc->sc_boardp;
468 	bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD);
469 
470 	return 0;
471 }
472 
473 int
rep_query_format(void * arg,audio_format_query_t * afp)474 rep_query_format(void *arg, audio_format_query_t *afp)
475 {
476 
477 	return audio_query_format(&repulse_format, 1, afp);
478 }
479 
480 /*
481  * XXX the following three functions need to be enhanced for the FPGA s/pdif
482  * mode. Generic ac97 versions for now.
483  */
484 
485 int
rep_get_port(void * arg,mixer_ctrl_t * cp)486 rep_get_port(void *arg, mixer_ctrl_t *cp)
487 {
488 	struct repulse_softc *sc;
489 
490 	sc = arg;
491 	return sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp);
492 }
493 
494 int
rep_set_port(void * arg,mixer_ctrl_t * cp)495 rep_set_port(void *arg, mixer_ctrl_t *cp)
496 {
497 	struct repulse_softc *sc;
498 
499 	sc = arg;
500 	return sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp);
501 }
502 
503 int
rep_query_devinfo(void * arg,mixer_devinfo_t * dip)504 rep_query_devinfo(void *arg, mixer_devinfo_t *dip)
505 {
506 	struct repulse_softc *sc;
507 
508 	sc = arg;
509 	return sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip);
510 }
511 
512 int
rep_round_blocksize(void * arg,int blk,int mode,const audio_params_t * param)513 rep_round_blocksize(void *arg, int blk, int mode, const audio_params_t *param)
514 {
515 	int b1;
516 
517 	b1 = (blk & -32);
518 
519 	if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */)
520 		b1 =  65536 / 2 / 2 / 4;
521 	return b1;
522 }
523 
524 void
rep_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)525 rep_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
526 {
527 	struct repulse_softc *sc = opaque;
528 
529 	*intr = &sc->sc_intr_lock;
530 	*thread = &sc->sc_lock;
531 }
532 
533 
534 int
rep_set_format(void * addr,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)535 rep_set_format(void *addr, int setmode,
536 	const audio_params_t *play, const audio_params_t *rec,
537 	audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
538 {
539 	struct repulse_softc *sc;
540 
541 	sc = addr;
542 	/* XXX 96kHz */
543 	if ((setmode & AUMODE_PLAY)) {
544 		repac_write(sc, AC97_REG_PCM_FRONT_DAC_RATE, play->sample_rate);
545 	}
546 	if ((setmode & AUMODE_RECORD)) {
547 		repac_write(sc, AC97_REG_PCM_LR_ADC_RATE, rec->sample_rate);
548 	}
549 
550 	return 0;
551 }
552 
553 
554 int
rep_start_output(void * addr,void * block,int blksize,void (* intr)(void *),void * intrarg)555 rep_start_output(void *addr, void *block, int blksize,
556 	void (*intr)(void*), void *intrarg)
557 {
558 	struct repulse_softc *sc;
559 	struct repulse_hw *bp;
560 	uint16_t status;
561 
562 
563 	sc = addr;
564 	bp = sc->sc_boardp;
565 
566 	/* TODO: prepare hw if necessary */
567 	status = bp->rhw_status;
568 	if (!(status & REPSTATUS_PLAY))
569 		bp->rhw_status = status |
570 		    REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST;
571 
572 	/* copy data */
573 	uint16_t *q = block;
574 	int length = blksize;
575 	while (length > 0) {
576 		bp->rhw_fifo_lh = *q++;
577 		bp->rhw_fifo_rh = *q++;
578 		length -= 4;
579 	}
580 
581 	/* TODO: set hw if necessary */
582 	if (intr) {
583 		bp->rhw_status |= REPSTATUS_PLAYIRQENABLE;
584 		bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2;
585 		/* /2: give us time to return on the first call */
586 	}
587 
588 	/* save callback function */
589 	sc->sc_playarg = intrarg;
590 	sc->sc_playmore = intr;
591 
592 	return 0;
593 }
594 
595 int
rep_start_input(void * addr,void * block,int blksize,void (* intr)(void *),void * intrarg)596 rep_start_input(void *addr, void *block, int blksize,
597 	void (*intr)(void*), void *intrarg)
598 {
599 	struct repulse_softc *sc;
600 	struct repulse_hw *bp;
601 	uint16_t status;
602 
603 	sc = addr;
604 	bp = sc->sc_boardp;
605 
606 	sc->sc_captbuf = block;
607 	sc->sc_captbufsz = blksize;
608 	sc->sc_captarg = intrarg;
609 	sc->sc_captmore = intr;
610 
611 	status = bp->rhw_status;
612 	if (!(status & REPSTATUS_RECORD))
613 		bp->rhw_status = status | REPSTATUS_RECORD
614 			| REPSTATUS_RECFIFORST;
615 
616 	bp->rhw_status |= REPSTATUS_RECIRQENABLE;
617 	bp->rhw_capt_fifosz = blksize / sc->sc_captscale;
618 
619 	return 0;
620 }
621 
622 /* irq handler */
623 
624 int
rep_intr(void * tag)625 rep_intr(void *tag)
626 {
627 	struct repulse_softc *sc;
628 	struct repulse_hw *bp;
629 	int foundone;
630 	uint16_t status;
631 
632 	foundone = 0;
633 
634 	sc = tag;
635 
636 	mutex_spin_enter(&sc->sc_intr_lock);
637 
638 	bp = sc->sc_boardp;
639 	status = bp->rhw_status;
640 
641 	if (status & REPSTATUS_PLAYIRQACK) {
642 		foundone = 1;
643 		status &= ~REPSTATUS_PLAYIRQENABLE;
644 		bp->rhw_status = status;
645 		(*sc->sc_playmore)(sc->sc_playarg);
646 	}
647 
648 	if (status & REPSTATUS_RECIRQACK) {
649 		foundone = 1;
650 		status &= ~REPSTATUS_RECIRQENABLE;
651 		bp->rhw_status = status;
652 		uint16_t *q = sc->sc_captbuf;
653 		int length = sc->sc_captbufsz;
654 		while (length > 0) {
655 			*q++ = bp->rhw_fifo_lh;
656 			*q++ = bp->rhw_fifo_rh;
657 			length -= 4;
658 		}
659 		(*sc->sc_captmore)(sc->sc_captarg);
660 	}
661 
662 	mutex_spin_exit(&sc->sc_intr_lock);
663 
664 	return foundone;
665 }
666