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