Home | History | Annotate | Line # | Download | only in dev
repulse.c revision 1.2.4.3
      1 /*	$NetBSD: repulse.c,v 1.2.4.3 2002/10/18 02:35:03 nathanw 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  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	  This product includes software developed by the NetBSD
     21  *	  Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 __KERNEL_RCSID(0, "$NetBSD: repulse.c,v 1.2.4.3 2002/10/18 02:35:03 nathanw Exp $");
     41 
     42 #include <sys/types.h>
     43 #include <sys/param.h>
     44 #include <sys/systm.h>
     45 #include <sys/kernel.h>
     46 #include <sys/device.h>
     47 #include <sys/fcntl.h>		/* FREAD */
     48 
     49 #include <machine/bus.h>
     50 
     51 #include <sys/audioio.h>
     52 #include <dev/audio_if.h>
     53 #include <dev/mulaw.h>
     54 
     55 #include <dev/ic/ac97reg.h>
     56 #include <dev/ic/ac97var.h>
     57 
     58 #include <amiga/dev/zbusvar.h>
     59 #include <amiga/amiga/isr.h>
     60 
     61 #include <amiga/dev/repulse_firmware.h>
     62 
     63 #ifndef vu_int8_t
     64 #define vu_int8_t volatile u_int8_t
     65 #endif
     66 #ifndef vu_int16_t
     67 #define vu_int16_t volatile u_int16_t
     68 #endif
     69 #ifndef vu_int32_t
     70 #define vu_int32_t volatile u_int32_t
     71 #endif
     72 
     73 /* ac97 attachment functions */
     74 
     75 int repac_attach(void *, struct ac97_codec_if *);
     76 int repac_read(void *, u_int8_t, u_int16_t *);
     77 int repac_write(void *, u_int8_t, u_int16_t);
     78 void repac_reset(void *);
     79 enum ac97_host_flag repac_flags(void *);
     80 
     81 /* audio attachment functions */
     82 
     83 int rep_open(void *, int);
     84 void rep_close(void *);
     85 int rep_getdev(void *, struct audio_device *);
     86 int rep_get_props(void *);
     87 int rep_halt_output(void *);
     88 int rep_halt_input(void *);
     89 int rep_query_encoding(void *, struct audio_encoding *);
     90 int rep_set_params(void *, int, int, struct audio_params *,
     91     struct audio_params *);
     92 int rep_round_blocksize(void *, int);
     93 int rep_set_port(void *, mixer_ctrl_t *);
     94 int rep_get_port(void *, mixer_ctrl_t *);
     95 int rep_query_devinfo(void *, mixer_devinfo_t *);
     96 size_t rep_round_buffersize(void *, int, size_t);
     97 
     98 int rep_start_input(void *, void *, int, void (*)(void *), void *);
     99 int rep_start_output(void *, void *, int, void (*)(void *), void *);
    100 
    101 int rep_intr(void *tag);
    102 
    103 
    104 /* audio attachment */
    105 
    106 struct audio_hw_if rep_hw_if = {
    107 	rep_open,
    108 	rep_close,
    109 	/* drain */ 0,
    110 	rep_query_encoding,
    111 	rep_set_params,
    112 	rep_round_blocksize,
    113 	/* commit_setting */ 0,
    114 	/* init_output */ 0,
    115 	/* init_input */ 0,
    116 	rep_start_output,
    117 	rep_start_input,
    118 	rep_halt_output,
    119 	rep_halt_input,
    120 	/* speaker_ctl */ 0,
    121 	rep_getdev,
    122 	/* getfd */ 0,
    123 	rep_set_port,
    124 	rep_get_port,
    125 	rep_query_devinfo,
    126 	/* allocm */ 0,
    127 	/* freem */ 0,
    128 	rep_round_buffersize,
    129 	/* mappage */ 0,
    130 	rep_get_props,
    131 	/* trigger_output */ 0,
    132 	/* trigger_input */ 0,
    133 	/* dev_ioctl */ 0,
    134 };
    135 
    136 /* hardware registers */
    137 
    138 struct repulse_hw {
    139 	vu_int16_t	rhw_status;
    140 	vu_int16_t	rhw_fifostatus;		/* 0xrrrrpppp0000flag */
    141 	vu_int16_t	rhw_reg_address;
    142 	vu_int16_t	rhw_reg_data;
    143 /* 0x08 */
    144 	vu_int16_t	rhw_fifo_lh;
    145 	vu_int16_t	rhw_fifo_ll;
    146 	vu_int16_t	rhw_fifo_rh;
    147 	vu_int16_t	rhw_fifo_rl;
    148 /* 0x10 */
    149 	vu_int16_t	rhw_fifo_pack;
    150 	vu_int16_t	rhw_play_fifosz;
    151 	vu_int32_t	rhw_spdifin_stat;
    152 #define	rhw_spdifout_stat rhw_spdifin_stat;
    153 
    154 /* 0x18 */
    155 	vu_int16_t	rhw_capt_fifosz;
    156 	vu_int16_t	rhw_version;
    157 	vu_int16_t	rhw_dummy1;
    158 	vu_int8_t	rhw_firmwareload;
    159 /* 0x1F */
    160 	vu_int8_t	rhw_dummy2[66 - 31];
    161 /* 0x42 */
    162 	vu_int16_t	rhw_reset;
    163 } /* __attribute__((packed)) */;
    164 
    165 #define REPSTATUS_PLAY		0x0001
    166 #define REPSTATUS_RECORD	0x0002
    167 #define REPSTATUS_PLAYFIFORST	0x0004
    168 #define REPSTATUS_RECFIFORST	0x0008
    169 
    170 #define REPSTATUS_REGSENDBUSY	0x0010
    171 #define REPSTATUS_LOOPBACK	0x0020
    172 #define REPSTATUS_ENSPDIFIN	0x0040
    173 #define REPSTATUS_ENSPDIFOUT	0x0080
    174 
    175 #define REPSTATUS_CODECRESET	0x0200
    176 #define REPSTATUS_SPDIFOUT24	0x0400
    177 #define REPSTATUS_SPDIFIN24	0x0800
    178 
    179 #define REPSTATUS_RECIRQENABLE	0x1000
    180 #define REPSTATUS_RECIRQACK	0x2000
    181 #define REPSTATUS_PLAYIRQENABLE	0x4000
    182 #define REPSTATUS_PLAYIRQACK	0x8000
    183 
    184 #define REPFIFO_PLAYFIFOFULL	0x0001
    185 #define REPFIFO_PLAYFIFOEMPTY	0x0002
    186 #define REPFIFO_RECFIFOFULL	0x0004
    187 #define REPFIFO_RECFIFOEMPTY	0x0008
    188 #define REPFIFO_PLAYFIFOGAUGE(x) ((x << 4) & 0xf000)
    189 #define REPFIFO_RECFIFOGAUGE(x)		(x & 0xf000)
    190 
    191 /* ac97 data stream transfer functions */
    192 void rep_read_16_stereo(struct repulse_hw *, u_int8_t *, int, unsigned);
    193 void rep_read_16_mono(struct repulse_hw *, u_int8_t *, int, unsigned);
    194 void rep_write_16_stereo(struct repulse_hw *, u_int8_t *, int, unsigned);
    195 void rep_write_16_mono(struct repulse_hw *, u_int8_t *, int, unsigned);
    196 void rep_read_8_stereo(struct repulse_hw *, u_int8_t *, int, unsigned);
    197 void rep_read_8_mono(struct repulse_hw *, u_int8_t *, int, unsigned);
    198 void rep_write_8_stereo(struct repulse_hw *, u_int8_t *, int, unsigned);
    199 void rep_write_8_mono(struct repulse_hw *, u_int8_t *, int, unsigned);
    200 
    201 /* AmigaDOS Delay() ticks */
    202 
    203 #define USECPERTICK	(1000000/50)
    204 
    205 /* NetBSD device attachment */
    206 
    207 struct repulse_softc {
    208 	struct device		sc_dev;
    209 	struct isr		sc_isr;
    210 	struct ac97_host_if	sc_achost;
    211 	struct ac97_codec_if	*sc_codec_if;
    212 
    213 	struct repulse_hw	*sc_boardp;
    214 
    215 	void	(*sc_captmore)(void *);
    216 	void	 *sc_captarg;
    217 
    218 	void	(*sc_captfun)(struct repulse_hw *, u_int8_t *, int, unsigned);
    219 	void	 *sc_captbuf;
    220 	int	  sc_captscale;
    221 	int	  sc_captbufsz;
    222 	unsigned  sc_captflags;
    223 
    224 
    225 	void	(*sc_playmore)(void *);
    226 	void	 *sc_playarg;
    227 	void	(*sc_playfun)(struct repulse_hw *, u_int8_t *, int, unsigned);
    228 	int	  sc_playscale;
    229 	unsigned  sc_playflags;
    230 
    231 };
    232 
    233 int repulse_match (struct device *, struct cfdata *, void *);
    234 void repulse_attach (struct device *, struct device *, void *);
    235 
    236 CFATTACH_DECL(repulse, sizeof(struct repulse_softc),
    237     repulse_match, repulse_attach, NULL, NULL);
    238 
    239 int
    240 repulse_match(struct device *parent, struct cfdata *cfp, void *aux) {
    241 	struct zbus_args *zap;
    242 
    243 	zap = aux;
    244 
    245 	if (zap->manid != 0x4144)
    246 		return (0);
    247 
    248 	if (zap->prodid != 0)
    249 		return (0);
    250 
    251 	return (1);
    252 }
    253 
    254 void
    255 repulse_attach(struct device *parent, struct device *self, void *aux) {
    256 	struct repulse_softc *sc;
    257 	struct zbus_args *zap;
    258 	struct repulse_hw *bp;
    259         struct mixer_ctrl ctl;
    260 	u_int8_t *fwp;
    261 	int needs_firmware;
    262 	int i;
    263 
    264 	u_int16_t a;
    265 
    266 	sc = (struct repulse_softc *)self;
    267 	zap = aux;
    268 	bp = (struct repulse_hw *)zap->va;
    269 	sc->sc_boardp = bp;
    270 
    271 	needs_firmware = 0;
    272 	if (bp->rhw_fifostatus & 0x00f0)
    273 		needs_firmware = 1;
    274 	else {
    275 		bp->rhw_status = 0x000c;
    276 		if (bp->rhw_status != 0 || bp->rhw_fifostatus != 0x0f0a)
    277 			needs_firmware = 1;
    278 	}
    279 
    280 	printf(": ");
    281 	if (needs_firmware) {
    282 		printf("loading ");
    283 		bp->rhw_reset = 0;
    284 
    285 		delay(1 * USECPERTICK);
    286 
    287 		for (fwp = (u_int8_t *)repulse_firmware;
    288 		    fwp < (repulse_firmware_size +
    289 		    (u_int8_t *)repulse_firmware); fwp++)
    290 			bp->rhw_firmwareload = *fwp;
    291 
    292 		delay(1 * USECPERTICK);
    293 
    294 		if (bp->rhw_fifostatus & 0x00f0)
    295 			goto Initerr;
    296 
    297 		a = /* bp->rhw_status;
    298 		a |= */ REPSTATUS_CODECRESET;
    299 		bp->rhw_status = a;
    300 
    301 		a = bp->rhw_status;
    302 		if ((a & REPSTATUS_CODECRESET) == 0)
    303 			goto Initerr;
    304 
    305 		(void)bp->rhw_status;
    306 		(void)bp->rhw_status;
    307 		(void)bp->rhw_status;
    308 		a = bp->rhw_status;
    309 		a &= ~REPSTATUS_CODECRESET;
    310 		bp->rhw_status = a;
    311 	}
    312 
    313 	printf("firmware version 0x%x\n", bp->rhw_version);
    314 
    315 	sc->sc_achost.arg = sc;
    316 
    317 	sc->sc_achost.reset = repac_reset;
    318 	sc->sc_achost.read = repac_read;
    319 	sc->sc_achost.write = repac_write;
    320 	sc->sc_achost.attach = repac_attach;
    321 	sc->sc_achost.flags = 0;
    322 
    323 	if (ac97_attach(&sc->sc_achost)) {
    324 		printf("%s: error attaching codec\n", self->dv_xname);
    325 		return;
    326 	}
    327 
    328 #ifdef DIAGNOSTIC
    329 	/*
    330 	 * Print a warning if the codec doesn't support hardware variable
    331 	 * rate audio. As the initial incarnations of the Repulse board
    332 	 * are AC'97 2.1, it is epxected that we'll always have VRA.
    333 	 */
    334 	/*
    335 	 * XXX this should be a panic(). OTOH, audio codec speed is not
    336 	 * important enough to do this.
    337 	 */
    338 	a = sc->sc_codec_if->vtbl->get_extcaps(sc->sc_codec_if);
    339 	if (!(a & AC97_EXT_AUDIO_VRA)) {
    340 		printf("%s: warning: codec doesn't support "
    341 		    "hardware AC'97 2.0 Variable Rate Audio\n",
    342 			sc->sc_dev.dv_xname);
    343 	}
    344 #endif
    345 
    346 	/*
    347 	 * from auvia.c: disable mutes ...
    348 	 * XXX maybe this should happen in MI code?
    349 	 */
    350 
    351 	for (i = 0; i < 5; i++) {
    352 		static struct {
    353 			char *class, *device;
    354 		} d[] = {
    355 			{ AudioCoutputs, AudioNmaster},
    356                         { AudioCinputs, AudioNdac},
    357                         { AudioCinputs, AudioNcd},
    358                         { AudioCinputs, AudioNline},
    359                         { AudioCrecord, AudioNvolume},
    360 		};
    361 
    362 		ctl.type = AUDIO_MIXER_ENUM;
    363 		ctl.un.ord = 0;
    364 		ctl.dev = sc->sc_codec_if->vtbl->get_portnum_by_name(
    365 			sc->sc_codec_if, d[i].class, d[i].device, AudioNmute);
    366 		rep_set_port(sc, &ctl);
    367 	}
    368 
    369 	sc->sc_isr.isr_ipl = 2;
    370 	sc->sc_isr.isr_arg = sc;
    371 	sc->sc_isr.isr_intr = rep_intr;
    372 	add_isr(&sc->sc_isr);
    373 
    374 	audio_attach_mi(&rep_hw_if, sc, &sc->sc_dev);
    375 
    376 	return;
    377 
    378 Initerr:
    379 	printf("\n%s: firmware not successfully loaded\n", self->dv_xname);
    380 	return;
    381 
    382 }
    383 
    384 void repac_reset(void *arg) {
    385 	struct repulse_softc *sc = arg;
    386 	struct repulse_hw *bp = sc->sc_boardp;
    387 
    388 	u_int16_t a;
    389 
    390 	a = bp->rhw_status;
    391 	a |= REPSTATUS_CODECRESET;
    392 	bp->rhw_status = a;
    393 
    394 	a = bp->rhw_status;
    395 #ifdef DIAGNOSTIC
    396 	if ((a & REPSTATUS_CODECRESET) == 0)
    397 		panic("%s: cannot set reset bit", sc->sc_dev.dv_xname);
    398 #endif
    399 
    400 	a = bp->rhw_status;
    401 	a = bp->rhw_status;
    402 	a = bp->rhw_status;
    403 	a = bp->rhw_status;
    404 	a &= ~REPSTATUS_CODECRESET;
    405 	bp->rhw_status = a;
    406 }
    407 
    408 int repac_read(void *arg, u_int8_t reg, u_int16_t *valuep) {
    409 	struct repulse_softc *sc = arg;
    410 	struct repulse_hw *bp = sc->sc_boardp;
    411 
    412 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
    413 	bp->rhw_reg_address = (reg & 0x7F) | 0x80;
    414 
    415 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
    416 
    417 	*valuep = bp->rhw_reg_data;
    418 
    419 	return 0;
    420 }
    421 
    422 int repac_write(void *arg, u_int8_t reg, u_int16_t value) {
    423 	struct repulse_softc *sc = arg;
    424 	struct repulse_hw *bp = sc->sc_boardp;
    425 
    426 	bp->rhw_reg_data = value;
    427 	bp->rhw_reg_address = reg & 0x7F;
    428 
    429 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
    430 
    431 	return 0;
    432 }
    433 
    434 int repac_attach(void *arg, struct ac97_codec_if *acip){
    435 
    436 	struct repulse_softc *sc;
    437 
    438 	sc = arg;
    439 	sc->sc_codec_if = acip;
    440 
    441 	return 0;
    442 }
    443 
    444 /* audio(9) support stuff which is not ac97-constant */
    445 
    446 int
    447 rep_open(void *arg, int flags)
    448 {
    449 	return 0;
    450 }
    451 
    452 void
    453 rep_close(void *arg)
    454 {
    455 	struct repulse_softc *sc = arg;
    456 
    457 	rep_halt_output(sc);
    458 	rep_halt_input(sc);
    459 }
    460 
    461 int
    462 rep_getdev(void *arg, struct audio_device *retp)
    463 {
    464 	struct repulse_softc *sc = arg;
    465 	struct repulse_hw *bp = sc->sc_boardp;
    466 
    467 	if (retp) {
    468 		strncpy(retp->name, "Repulse", sizeof(retp->name));
    469 		snprintf(retp->version, sizeof(retp->version), "0x%x",
    470 			bp->rhw_version);
    471 		strncpy(retp->config, "", sizeof(retp->config));
    472 	}
    473 
    474 	return 0;
    475 }
    476 
    477 int
    478 rep_get_props(void *v)
    479 {
    480 	return (AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX);
    481 }
    482 
    483 int
    484 rep_halt_output(void *arg)
    485 {
    486 	struct repulse_softc *sc = arg;
    487 	struct repulse_hw *bp = sc->sc_boardp;
    488 
    489 	bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY);
    490 
    491 
    492 	return 0;
    493 }
    494 
    495 int
    496 rep_halt_input(void *arg)
    497 {
    498 	struct repulse_softc *sc = arg;
    499 	struct repulse_hw *bp = sc->sc_boardp;
    500 
    501 	bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD);
    502 
    503 	return 0;
    504 }
    505 
    506 /*
    507  * Encoding support.
    508  *
    509  * TODO: add 24bit and 32bit modes here and in setparams.
    510  */
    511 
    512 const struct repulse_encoding_query {
    513 	const char *name;
    514 	int encoding, precision, flags;
    515 } rep_encoding_queries[] = {
    516 	{ AudioEulinear, AUDIO_ENCODING_ULINEAR, 8, 0},
    517 	{ AudioEmulaw,	AUDIO_ENCODING_ULAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
    518 	{ AudioEalaw,	AUDIO_ENCODING_ALAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
    519 	{ AudioEslinear, AUDIO_ENCODING_SLINEAR, 8, 0},
    520 	{ AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16, 0},
    521 	{ AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE, 16, 0},
    522 	{ AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE, 16, 0},
    523 	{ AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16, 0},
    524 };
    525 
    526 int
    527 rep_query_encoding(void *arg, struct audio_encoding *fp)
    528 {
    529 	int i;
    530 	const struct repulse_encoding_query *p;
    531 
    532 	i = fp->index;
    533 
    534 	if (i >= sizeof(rep_encoding_queries) /
    535 	    sizeof(struct repulse_encoding_query))
    536 		return (EINVAL);
    537 
    538 	p = &rep_encoding_queries[i];
    539 
    540 	strncpy (fp->name, p->name, sizeof(fp->name));
    541 	fp->encoding = p->encoding;
    542 	fp->precision = p->precision;
    543 	fp->flags = p->flags;
    544 
    545 	return (0);
    546 }
    547 
    548 /*
    549  * XXX the following three functions need to be enhanced for the FPGA s/pdif
    550  * mode. Generic ac97 versions for now.
    551  */
    552 
    553 int
    554 rep_get_port(void *arg, mixer_ctrl_t *cp)
    555 {
    556 	struct repulse_softc *sc = arg;
    557 
    558 	return (sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp));
    559 }
    560 
    561 int
    562 rep_set_port(void *arg, mixer_ctrl_t *cp)
    563 {
    564 	struct repulse_softc *sc = arg;
    565 
    566 	return (sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp));
    567 }
    568 
    569 int
    570 rep_query_devinfo (void *arg, mixer_devinfo_t *dip)
    571 {
    572 	struct repulse_softc *sc = arg;
    573 
    574 	return (sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip));
    575 }
    576 
    577 int
    578 rep_round_blocksize(void *arg, int blk)
    579 {
    580 	int b1;
    581 
    582 	b1 = (blk & -32);
    583 
    584 	if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */)
    585 		b1 =  65536 / 2 / 2 / 4;
    586 	return (b1);
    587 }
    588 
    589 size_t
    590 rep_round_buffersize(void *arg, int direction, size_t size)
    591 {
    592 	return size;
    593 }
    594 
    595 
    596 int
    597 rep_set_params(void *addr, int setmode, int usemode,
    598 	struct audio_params *play, struct audio_params *rec)
    599 {
    600 	struct repulse_softc *sc = addr;
    601 	struct audio_params *p;
    602 	int mode, reg;
    603 	unsigned  flags;
    604 	u_int16_t a;
    605 
    606 	/* for mode in (RECORD, PLAY) */
    607 	for (mode = AUMODE_RECORD; mode != -1;
    608 	    mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
    609 
    610 		if ((setmode & mode) == 0)
    611 		     continue;
    612 
    613 		p = mode == AUMODE_PLAY ? play : rec;
    614 
    615 		/* TODO XXX we can do upto 32bit, 96000 */
    616 		if (p->sample_rate < 4000 || p->sample_rate > 48000 ||
    617 		    (p->precision != 8 && p->precision != 16) ||
    618 		    (p->channels != 1 && p->channels != 2))
    619 			return (EINVAL);
    620 
    621 		reg = mode == AUMODE_PLAY ?
    622 			AC97_REG_PCM_FRONT_DAC_RATE : AC97_REG_PCM_LR_ADC_RATE;
    623 
    624 		repac_write(sc, reg, (u_int16_t) p->sample_rate);
    625 		repac_read(sc, reg, &a);
    626 		p->sample_rate = a;
    627 
    628 		if (mode == AUMODE_PLAY)
    629 			sc->sc_playscale = p->channels * p->precision / 8;
    630 		else
    631 			sc->sc_captscale = p->channels * p->precision / 8;
    632 
    633 		p->factor = 1;
    634 		p->sw_code = 0;
    635 
    636 		/* everything else is software, alas... */
    637 		/* XXX TBD signed/unsigned, *law, etc */
    638 
    639 		flags = 0;
    640 		if (p->encoding == AUDIO_ENCODING_ULINEAR_LE ||
    641 		    p->encoding == AUDIO_ENCODING_ULINEAR_BE ||
    642 		    p->encoding == AUDIO_ENCODING_ULINEAR)
    643 			flags |= 1;
    644 
    645 		if (p->encoding == AUDIO_ENCODING_SLINEAR_LE ||
    646 		    p->encoding == AUDIO_ENCODING_ULINEAR_LE)
    647 			flags |= 2;
    648 
    649 		if (mode == AUMODE_PLAY) {
    650 			sc->sc_playflags = flags;
    651 			if (p->encoding == AUDIO_ENCODING_ULAW) {
    652 				sc->sc_playfun = p->channels == 1 ?
    653 					rep_write_16_mono :
    654 					rep_write_16_stereo;
    655 				sc->sc_playflags = 0;
    656 				sc->sc_playscale = p->channels * 2;
    657 				p->sw_code = mulaw_to_slinear16_be;
    658 				p->factor = 2;
    659 			} else
    660 			if (p->encoding == AUDIO_ENCODING_ALAW) {
    661 				sc->sc_playfun = p->channels == 1 ?
    662 					rep_write_16_mono :
    663 					rep_write_16_stereo;
    664 				sc->sc_playflags = 0;
    665 				sc->sc_playscale = p->channels * 2;
    666 				p->sw_code = alaw_to_slinear16_be;
    667 				p->factor = 2;
    668 			} else
    669 			if (p->precision == 8 && p->channels == 1)
    670 				sc->sc_playfun = rep_write_8_mono;
    671 			else if (p->precision == 8 && p->channels == 2)
    672 				sc->sc_playfun = rep_write_8_stereo;
    673 			else if (p->precision == 16 && p->channels == 1)
    674 				sc->sc_playfun = rep_write_16_mono;
    675 			else if (p->precision == 16 && p->channels == 2)
    676 				sc->sc_playfun = rep_write_16_stereo;
    677 		} else {
    678 			sc->sc_captflags = flags;
    679 			if (p->encoding == AUDIO_ENCODING_ULAW) {
    680 				sc->sc_captfun = p->channels == 1 ?
    681 					rep_read_8_mono :
    682 					rep_read_8_stereo;
    683 				sc->sc_captflags = 0;
    684 				p->sw_code = slinear8_to_mulaw;
    685 				p->factor = 1;
    686 			} else
    687 			if (p->encoding == AUDIO_ENCODING_ALAW) {
    688 				sc->sc_captfun = p->channels == 1 ?
    689 					rep_read_8_mono :
    690 					rep_read_8_stereo;
    691 				sc->sc_captflags = 0;
    692 				p->sw_code = slinear8_to_alaw;
    693 				p->factor = 1;
    694 			} else
    695 			if (p->precision == 8 && p->channels == 1)
    696 				sc->sc_captfun = rep_read_8_mono;
    697 			else if (p->precision == 8 && p->channels == 2)
    698 				sc->sc_captfun = rep_read_8_stereo;
    699 			else if (p->precision == 16 && p->channels == 1)
    700 				sc->sc_captfun = rep_read_16_mono;
    701 			else if (p->precision == 16 && p->channels == 2)
    702 				sc->sc_captfun = rep_read_16_stereo;
    703 		}
    704 		/* TBD: ulaw, alaw */
    705 	}
    706 	return 0;
    707 }
    708 
    709 void
    710 rep_write_8_mono(struct repulse_hw *bp, u_int8_t *p, int length,
    711 	unsigned flags)
    712 {
    713 	u_int16_t sample;
    714 	u_int16_t xor;
    715 
    716 	xor = flags & 1 ? 0x8000 : 0;
    717 
    718 	bp->rhw_fifo_pack = 0;
    719 
    720 	while (length-- > 0) {
    721 		sample = ((*p++) << 8) ^ xor;
    722 		bp->rhw_fifo_lh = sample;
    723 		bp->rhw_fifo_rh = sample;
    724 	}
    725 }
    726 
    727 void
    728 rep_write_8_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
    729 	unsigned flags)
    730 {
    731 	u_int16_t xor;
    732 
    733 	xor = flags & 1 ? 0x8000 : 0;
    734 
    735 	bp->rhw_fifo_pack = 0;
    736 
    737 	while (length-- > 0) {
    738 		bp->rhw_fifo_lh = ((*p++) << 8) ^ xor;
    739 		bp->rhw_fifo_rh = ((*p++) << 8) ^ xor;
    740 	}
    741 }
    742 
    743 void
    744 rep_write_16_mono(struct repulse_hw *bp, u_int8_t *p, int length,
    745 	unsigned flags)
    746 {
    747 	u_int16_t *q = (u_int16_t *)p;
    748 	u_int16_t sample;
    749 	u_int16_t xor;
    750 
    751 	xor = flags & 1 ? 0x8000 : 0;
    752 
    753 	bp->rhw_fifo_pack = 0;
    754 
    755 	if (flags & 2) {
    756 		while (length > 0) {
    757 			sample = bswap16(*q++) ^ xor;
    758 			bp->rhw_fifo_lh = sample;
    759 			bp->rhw_fifo_rh = sample;
    760 			length -= 2;
    761 		}
    762 		return;
    763 	}
    764 
    765 	while (length > 0) {
    766 		sample = (*q++) ^ xor;
    767 		bp->rhw_fifo_lh = sample;
    768 		bp->rhw_fifo_rh = sample;
    769 		length -= 2;
    770 	}
    771 }
    772 
    773 void
    774 rep_write_16_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
    775 	unsigned flags)
    776 {
    777 	u_int16_t *q = (u_int16_t *)p;
    778 	u_int16_t xor;
    779 
    780 	xor = flags & 1 ? 0x8000 : 0;
    781 
    782 	bp->rhw_fifo_pack = 0;
    783 
    784 	if (flags & 2) {
    785 		while (length > 0) {
    786 			bp->rhw_fifo_lh = bswap16(*q++) ^ xor;
    787 			bp->rhw_fifo_rh = bswap16(*q++) ^ xor;
    788 			length -= 4;
    789 		}
    790 		return;
    791 	}
    792 	while (length > 0) {
    793 		bp->rhw_fifo_lh = (*q++) ^ xor;
    794 		bp->rhw_fifo_rh = (*q++) ^ xor;
    795 		length -= 4;
    796 	}
    797 }
    798 
    799 void
    800 rep_read_8_mono(struct	repulse_hw  *bp, u_int8_t *p, int length,
    801 	unsigned flags)
    802 {
    803 	u_int16_t v;
    804 	u_int16_t xor;
    805 
    806 	xor = flags & 1 ? 0x8000 : 0;
    807 
    808 	while (length > 0) {
    809 		*p++ = (bp->rhw_fifo_lh ^ xor) >> 8;
    810 		v    = bp->rhw_fifo_rh;
    811 		length--;
    812 	}
    813 }
    814 
    815 void
    816 rep_read_16_mono(struct	 repulse_hw  *bp, u_int8_t *p, int length,
    817 	unsigned flags)
    818 {
    819 	u_int16_t *q = (u_int16_t *)p;
    820 	u_int16_t v;
    821 	u_int16_t xor;
    822 
    823 	xor = flags & 1 ? 0x8000 : 0;
    824 
    825 	if (flags & 2) {
    826 		while (length > 0) {
    827 			*q++ = bswap16(bp->rhw_fifo_lh ^ xor);
    828 			v    = bp->rhw_fifo_rh;
    829 			length -= 2;
    830 		}
    831 		return;
    832 	}
    833 
    834 	while (length > 0) {
    835 		*q++ = bp->rhw_fifo_lh ^ xor;
    836 		v    = bp->rhw_fifo_rh;
    837 		length -= 2;
    838 	}
    839 }
    840 
    841 void
    842 rep_read_8_stereo(struct  repulse_hw  *bp, u_int8_t *p, int length,
    843 	unsigned flags)
    844 {
    845 	u_int16_t xor;
    846 
    847 	xor = flags & 1 ? 0x8000 : 0;
    848 	while (length > 0) {
    849 		*p++ = (bp->rhw_fifo_lh ^ xor) >> 8;
    850 		*p++ = (bp->rhw_fifo_rh ^ xor) >> 8;
    851 		length -= 2;
    852 	}
    853 }
    854 
    855 void
    856 rep_read_16_stereo(struct  repulse_hw  *bp, u_int8_t *p, int length,
    857 	unsigned flags)
    858 {
    859 	u_int16_t *q = (u_int16_t *)p;
    860 	u_int16_t xor;
    861 
    862 	xor = flags & 1 ? 0x8000 : 0;
    863 
    864 	if (flags & 2) {
    865 		while (length > 0) {
    866 			*q++ = bswap16(bp->rhw_fifo_lh ^ xor);
    867 			*q++ = bswap16(bp->rhw_fifo_rh ^ xor);
    868 			length -= 4;
    869 		}
    870 		return;
    871 	}
    872 	while (length > 0) {
    873 		*q++ = bp->rhw_fifo_lh ^ xor;
    874 		*q++ = bp->rhw_fifo_rh ^ xor;
    875 		length -= 4;
    876 	}
    877 }
    878 
    879 /*
    880  * At this point the transfer function is set.
    881  */
    882 
    883 int
    884 rep_start_output(void *addr, void *block, int blksize,
    885 	void (*intr)(void*), void *intrarg) {
    886 
    887 	struct repulse_softc *sc;
    888 	u_int8_t *buf;
    889 	struct repulse_hw *bp;
    890 	u_int16_t status;
    891 
    892 
    893 	sc = addr;
    894 	bp = sc->sc_boardp;
    895 	buf = block;
    896 
    897 	/* TODO: prepare hw if necessary */
    898 	status = bp->rhw_status;
    899 	if (!(status & REPSTATUS_PLAY))
    900 		bp->rhw_status = status |
    901 		    REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST;
    902 
    903 	/* copy data */
    904 	(*sc->sc_playfun)(bp, buf, blksize, sc->sc_playflags);
    905 
    906 	/* TODO: set hw if necessary */
    907 	if (intr) {
    908 		bp->rhw_status |= REPSTATUS_PLAYIRQENABLE;
    909 		bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2;
    910 		/* /2: give us time to return on the first call */
    911 	}
    912 
    913 	/* save callback function */
    914 	sc->sc_playarg = intrarg;
    915 	sc->sc_playmore = intr;
    916 
    917 	return 0;
    918 }
    919 
    920 int
    921 rep_start_input(void *addr, void *block, int blksize,
    922 	void (*intr)(void*), void *intrarg) {
    923 
    924 	struct repulse_softc *sc;
    925 	struct repulse_hw *bp;
    926 	u_int16_t status;
    927 
    928 	sc = addr;
    929 	bp = sc->sc_boardp;
    930 
    931 	sc->sc_captbuf = block;
    932 	sc->sc_captbufsz = blksize;
    933 	sc->sc_captarg = intrarg;
    934 	sc->sc_captmore = intr;
    935 
    936 	status = bp->rhw_status;
    937 	if (!(status & REPSTATUS_RECORD))
    938 		bp->rhw_status = status | REPSTATUS_RECORD
    939 			| REPSTATUS_RECFIFORST;
    940 
    941 	bp->rhw_status |= REPSTATUS_RECIRQENABLE;
    942 	bp->rhw_capt_fifosz = blksize / sc->sc_captscale;
    943 
    944 	return 0;
    945 }
    946 
    947 /* irq handler */
    948 
    949 int
    950 rep_intr(void *tag) {
    951 	struct repulse_softc *sc;
    952 	struct repulse_hw *bp;
    953 	int foundone;
    954 	u_int16_t status;
    955 
    956 	foundone = 0;
    957 
    958 	sc = tag;
    959 	bp = sc->sc_boardp;
    960 	status = bp->rhw_status;
    961 
    962 	if (status & REPSTATUS_PLAYIRQACK) {
    963 		foundone = 1;
    964 		status &= ~REPSTATUS_PLAYIRQENABLE;
    965 		bp->rhw_status = status;
    966 		(*sc->sc_playmore)(sc->sc_playarg);
    967 	}
    968 
    969 	if (status & REPSTATUS_RECIRQACK) {
    970 		foundone = 1;
    971 		status &= ~REPSTATUS_RECIRQENABLE;
    972 		bp->rhw_status = status;
    973 		(*sc->sc_captfun)(bp, sc->sc_captbuf, sc->sc_captbufsz,
    974 			sc->sc_captflags);
    975 		(*sc->sc_captmore)(sc->sc_captarg);
    976 	}
    977 
    978 	return foundone;
    979 }
    980