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