xref: /netbsd-src/sys/arch/amiga/dev/repulse.c (revision 9fbd88883c38d0c0fbfcbe66d76fe6b0fab3f9de)
1 /*	$NetBSD: repulse.c,v 1.4 2002/01/28 09:57:02 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 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: repulse.c,v 1.4 2002/01/28 09:57:02 aymeric 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 struct cfattach repulse_ca = {
237 	sizeof(struct repulse_softc), repulse_match, repulse_attach
238 };
239 
240 int
241 repulse_match(struct device *parent, struct cfdata *cfp, void *aux) {
242 	struct zbus_args *zap;
243 
244 	zap = aux;
245 
246 	if (zap->manid != 0x4144)
247 		return (0);
248 
249 	if (zap->prodid != 0)
250 		return (0);
251 
252 	return (1);
253 }
254 
255 void
256 repulse_attach(struct device *parent, struct device *self, void *aux) {
257 	struct repulse_softc *sc;
258 	struct zbus_args *zap;
259 	struct repulse_hw *bp;
260         struct mixer_ctrl ctl;
261 	u_int8_t *fwp;
262 	int needs_firmware;
263 	int i;
264 
265 	u_int16_t a;
266 
267 	sc = (struct repulse_softc *)self;
268 	zap = aux;
269 	bp = (struct repulse_hw *)zap->va;
270 	sc->sc_boardp = bp;
271 
272 	needs_firmware = 0;
273 	if (bp->rhw_fifostatus & 0x00f0)
274 		needs_firmware = 1;
275 	else {
276 		bp->rhw_status = 0x000c;
277 		if (bp->rhw_status != 0 || bp->rhw_fifostatus != 0x0f0a)
278 			needs_firmware = 1;
279 	}
280 
281 	printf(": ");
282 	if (needs_firmware) {
283 		printf("loading ");
284 		bp->rhw_reset = 0;
285 
286 		delay(1 * USECPERTICK);
287 
288 		for (fwp = (u_int8_t *)repulse_firmware;
289 		    fwp < (repulse_firmware_size +
290 		    (u_int8_t *)repulse_firmware); fwp++)
291 			bp->rhw_firmwareload = *fwp;
292 
293 		delay(1 * USECPERTICK);
294 
295 		if (bp->rhw_fifostatus & 0x00f0)
296 			goto Initerr;
297 
298 		a = /* bp->rhw_status;
299 		a |= */ REPSTATUS_CODECRESET;
300 		bp->rhw_status = a;
301 
302 		a = bp->rhw_status;
303 		if ((a & REPSTATUS_CODECRESET) == 0)
304 			goto Initerr;
305 
306 		(void)bp->rhw_status;
307 		(void)bp->rhw_status;
308 		(void)bp->rhw_status;
309 		a = bp->rhw_status;
310 		a &= ~REPSTATUS_CODECRESET;
311 		bp->rhw_status = a;
312 	}
313 
314 	printf("firmware version 0x%x\n", bp->rhw_version);
315 
316 	sc->sc_achost.arg = sc;
317 
318 	sc->sc_achost.reset = repac_reset;
319 	sc->sc_achost.read = repac_read;
320 	sc->sc_achost.write = repac_write;
321 	sc->sc_achost.attach = repac_attach;
322 	sc->sc_achost.flags = 0;
323 
324 	if (ac97_attach(&sc->sc_achost)) {
325 		printf("%s: error attaching codec\n", self->dv_xname);
326 		return;
327 	}
328 
329 #ifdef DIAGNOSTIC
330 	/*
331 	 * Print a warning if the codec doesn't support hardware variable
332 	 * rate audio. As the initial incarnations of the Repulse board
333 	 * are AC'97 2.1, it is epxected that we'll always have VRA.
334 	 */
335 	/*
336 	 * XXX this should be a panic(). OTOH, audio codec speed is not
337 	 * important enough to do this.
338 	 */
339 	if (repac_read(sc, AC97_REG_EXTENDED_ID, &a)
340 		|| !(a & AC97_CODEC_DOES_VRA)) {
341 		printf("%s: warning: codec doesn't support "
342 		    "hardware AC'97 2.0 Variable Rate Audio\n",
343 			sc->sc_dev.dv_xname);
344 	}
345 #endif
346 	/* enable VRA */
347 	repac_write(sc, AC97_REG_EXTENDED_STATUS,
348 		AC97_ENAB_VRA | AC97_ENAB_MICVRA);
349 
350 	/*
351 	 * from auvia.c: disable mutes ...
352 	 * XXX maybe this should happen in MI code?
353 	 */
354 
355 	for (i = 0; i < 5; i++) {
356 		static struct {
357 			char *class, *device;
358 		} d[] = {
359 			{ AudioCoutputs, AudioNmaster},
360                         { AudioCinputs, AudioNdac},
361                         { AudioCinputs, AudioNcd},
362                         { AudioCinputs, AudioNline},
363                         { AudioCrecord, AudioNvolume},
364 		};
365 
366 		ctl.type = AUDIO_MIXER_ENUM;
367 		ctl.un.ord = 0;
368 		ctl.dev = sc->sc_codec_if->vtbl->get_portnum_by_name(
369 			sc->sc_codec_if, d[i].class, d[i].device, AudioNmute);
370 		rep_set_port(sc, &ctl);
371 	}
372 
373 	sc->sc_isr.isr_ipl = 2;
374 	sc->sc_isr.isr_arg = sc;
375 	sc->sc_isr.isr_intr = rep_intr;
376 	add_isr(&sc->sc_isr);
377 
378 	audio_attach_mi(&rep_hw_if, sc, &sc->sc_dev);
379 
380 	return;
381 
382 Initerr:
383 	printf("\n%s: firmware not successfully loaded\n", self->dv_xname);
384 	return;
385 
386 }
387 
388 void repac_reset(void *arg) {
389 	struct repulse_softc *sc = arg;
390 	struct repulse_hw *bp = sc->sc_boardp;
391 
392 	u_int16_t a;
393 
394 	a = bp->rhw_status;
395 	a |= REPSTATUS_CODECRESET;
396 	bp->rhw_status = a;
397 
398 	a = bp->rhw_status;
399 #ifdef DIAGNOSTIC
400 	if ((a & REPSTATUS_CODECRESET) == 0)
401 		panic("%s: cannot set reset bit", sc->sc_dev.dv_xname);
402 #endif
403 
404 	a = bp->rhw_status;
405 	a = bp->rhw_status;
406 	a = bp->rhw_status;
407 	a = bp->rhw_status;
408 	a &= ~REPSTATUS_CODECRESET;
409 	bp->rhw_status = a;
410 }
411 
412 int repac_read(void *arg, u_int8_t reg, u_int16_t *valuep) {
413 	struct repulse_softc *sc = arg;
414 	struct repulse_hw *bp = sc->sc_boardp;
415 
416 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
417 	bp->rhw_reg_address = (reg & 0x7F) | 0x80;
418 
419 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
420 
421 	*valuep = bp->rhw_reg_data;
422 
423 	return 0;
424 }
425 
426 int repac_write(void *arg, u_int8_t reg, u_int16_t value) {
427 	struct repulse_softc *sc = arg;
428 	struct repulse_hw *bp = sc->sc_boardp;
429 
430 	bp->rhw_reg_data = value;
431 	bp->rhw_reg_address = reg & 0x7F;
432 
433 	while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
434 
435 	return 0;
436 }
437 
438 int repac_attach(void *arg, struct ac97_codec_if *acip){
439 
440 	struct repulse_softc *sc;
441 
442 	sc = arg;
443 	sc->sc_codec_if = acip;
444 
445 	return 0;
446 }
447 
448 /* audio(9) support stuff which is not ac97-constant */
449 
450 int
451 rep_open(void *arg, int flags)
452 {
453 	return 0;
454 }
455 
456 void
457 rep_close(void *arg)
458 {
459 	struct repulse_softc *sc = arg;
460 
461 	rep_halt_output(sc);
462 	rep_halt_input(sc);
463 }
464 
465 int
466 rep_getdev(void *arg, struct audio_device *retp)
467 {
468 	struct repulse_softc *sc = arg;
469 	struct repulse_hw *bp = sc->sc_boardp;
470 
471 	if (retp) {
472 		strncpy(retp->name, "Repulse", sizeof(retp->name));
473 		snprintf(retp->version, sizeof(retp->version), "0x%x",
474 			bp->rhw_version);
475 		strncpy(retp->config, "", sizeof(retp->config));
476 	}
477 
478 	return 0;
479 }
480 
481 int
482 rep_get_props(void *v)
483 {
484 	return (AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX);
485 }
486 
487 int
488 rep_halt_output(void *arg)
489 {
490 	struct repulse_softc *sc = arg;
491 	struct repulse_hw *bp = sc->sc_boardp;
492 
493 	bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY);
494 
495 
496 	return 0;
497 }
498 
499 int
500 rep_halt_input(void *arg)
501 {
502 	struct repulse_softc *sc = arg;
503 	struct repulse_hw *bp = sc->sc_boardp;
504 
505 	bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD);
506 
507 	return 0;
508 }
509 
510 /*
511  * Encoding support.
512  *
513  * TODO: add 24bit and 32bit modes here and in setparams.
514  */
515 
516 const struct repulse_encoding_query {
517 	const char *name;
518 	int encoding, precision, flags;
519 } rep_encoding_queries[] = {
520 	{ AudioEulinear, AUDIO_ENCODING_ULINEAR, 8, 0},
521 	{ AudioEmulaw,	AUDIO_ENCODING_ULAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
522 	{ AudioEalaw,	AUDIO_ENCODING_ALAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
523 	{ AudioEslinear, AUDIO_ENCODING_SLINEAR, 8, 0},
524 	{ AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16, 0},
525 	{ AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE, 16, 0},
526 	{ AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE, 16, 0},
527 	{ AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16, 0},
528 };
529 
530 int
531 rep_query_encoding(void *arg, struct audio_encoding *fp)
532 {
533 	int i;
534 	const struct repulse_encoding_query *p;
535 
536 	i = fp->index;
537 
538 	if (i >= sizeof(rep_encoding_queries) /
539 	    sizeof(struct repulse_encoding_query))
540 		return (EINVAL);
541 
542 	p = &rep_encoding_queries[i];
543 
544 	strncpy (fp->name, p->name, sizeof(fp->name));
545 	fp->encoding = p->encoding;
546 	fp->precision = p->precision;
547 	fp->flags = p->flags;
548 
549 	return (0);
550 }
551 
552 /*
553  * XXX the following three functions need to be enhanced for the FPGA s/pdif
554  * mode. Generic ac97 versions for now.
555  */
556 
557 int
558 rep_get_port(void *arg, mixer_ctrl_t *cp)
559 {
560 	struct repulse_softc *sc = arg;
561 
562 	return (sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp));
563 }
564 
565 int
566 rep_set_port(void *arg, mixer_ctrl_t *cp)
567 {
568 	struct repulse_softc *sc = arg;
569 
570 	return (sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp));
571 }
572 
573 int
574 rep_query_devinfo (void *arg, mixer_devinfo_t *dip)
575 {
576 	struct repulse_softc *sc = arg;
577 
578 	return (sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip));
579 }
580 
581 int
582 rep_round_blocksize(void *arg, int blk)
583 {
584 	int b1;
585 
586 	b1 = (blk & -32);
587 
588 	if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */)
589 		b1 =  65536 / 2 / 2 / 4;
590 	return (b1);
591 }
592 
593 size_t
594 rep_round_buffersize(void *arg, int direction, size_t size)
595 {
596 	return size;
597 }
598 
599 
600 int
601 rep_set_params(void *addr, int setmode, int usemode,
602 	struct audio_params *play, struct audio_params *rec)
603 {
604 	struct repulse_softc *sc = addr;
605 	struct audio_params *p;
606 	int mode, reg;
607 	unsigned  flags;
608 	u_int16_t a;
609 
610 	/* for mode in (RECORD, PLAY) */
611 	for (mode = AUMODE_RECORD; mode != -1;
612 	    mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
613 
614 		if ((setmode & mode) == 0)
615 		     continue;
616 
617 		p = mode == AUMODE_PLAY ? play : rec;
618 
619 		/* TODO XXX we can do upto 32bit, 96000 */
620 		if (p->sample_rate < 4000 || p->sample_rate > 48000 ||
621 		    (p->precision != 8 && p->precision != 16) ||
622 		    (p->channels != 1 && p->channels != 2))
623 			return (EINVAL);
624 
625 		reg = mode == AUMODE_PLAY ?
626 			AC97_REG_PCM_FRONT_DAC_RATE : AC97_REG_PCM_LR_ADC_RATE;
627 
628 		repac_write(sc, reg, (u_int16_t) p->sample_rate);
629 		repac_read(sc, reg, &a);
630 		p->sample_rate = a;
631 
632 		if (mode == AUMODE_PLAY)
633 			sc->sc_playscale = p->channels * p->precision / 8;
634 		else
635 			sc->sc_captscale = p->channels * p->precision / 8;
636 
637 		p->factor = 1;
638 		p->sw_code = 0;
639 
640 		/* everything else is software, alas... */
641 		/* XXX TBD signed/unsigned, *law, etc */
642 
643 		flags = 0;
644 		if (p->encoding == AUDIO_ENCODING_ULINEAR_LE ||
645 		    p->encoding == AUDIO_ENCODING_ULINEAR_BE ||
646 		    p->encoding == AUDIO_ENCODING_ULINEAR)
647 			flags |= 1;
648 
649 		if (p->encoding == AUDIO_ENCODING_SLINEAR_LE ||
650 		    p->encoding == AUDIO_ENCODING_ULINEAR_LE)
651 			flags |= 2;
652 
653 		if (mode == AUMODE_PLAY) {
654 			sc->sc_playflags = flags;
655 			if (p->encoding == AUDIO_ENCODING_ULAW) {
656 				sc->sc_playfun = p->channels == 1 ?
657 					rep_write_16_mono :
658 					rep_write_16_stereo;
659 				sc->sc_playflags = 0;
660 				sc->sc_playscale = p->channels * 2;
661 				p->sw_code = mulaw_to_slinear16_be;
662 				p->factor = 2;
663 			} else
664 			if (p->encoding == AUDIO_ENCODING_ALAW) {
665 				sc->sc_playfun = p->channels == 1 ?
666 					rep_write_16_mono :
667 					rep_write_16_stereo;
668 				sc->sc_playflags = 0;
669 				sc->sc_playscale = p->channels * 2;
670 				p->sw_code = alaw_to_slinear16_be;
671 				p->factor = 2;
672 			} else
673 			if (p->precision == 8 && p->channels == 1)
674 				sc->sc_playfun = rep_write_8_mono;
675 			else if (p->precision == 8 && p->channels == 2)
676 				sc->sc_playfun = rep_write_8_stereo;
677 			else if (p->precision == 16 && p->channels == 1)
678 				sc->sc_playfun = rep_write_16_mono;
679 			else if (p->precision == 16 && p->channels == 2)
680 				sc->sc_playfun = rep_write_16_stereo;
681 		} else {
682 			sc->sc_captflags = flags;
683 			if (p->encoding == AUDIO_ENCODING_ULAW) {
684 				sc->sc_captfun = p->channels == 1 ?
685 					rep_read_8_mono :
686 					rep_read_8_stereo;
687 				sc->sc_captflags = 0;
688 				p->sw_code = slinear8_to_mulaw;
689 				p->factor = 1;
690 			} else
691 			if (p->encoding == AUDIO_ENCODING_ALAW) {
692 				sc->sc_captfun = p->channels == 1 ?
693 					rep_read_8_mono :
694 					rep_read_8_stereo;
695 				sc->sc_captflags = 0;
696 				p->sw_code = slinear8_to_alaw;
697 				p->factor = 1;
698 			} else
699 			if (p->precision == 8 && p->channels == 1)
700 				sc->sc_captfun = rep_read_8_mono;
701 			else if (p->precision == 8 && p->channels == 2)
702 				sc->sc_captfun = rep_read_8_stereo;
703 			else if (p->precision == 16 && p->channels == 1)
704 				sc->sc_captfun = rep_read_16_mono;
705 			else if (p->precision == 16 && p->channels == 2)
706 				sc->sc_captfun = rep_read_16_stereo;
707 		}
708 		/* TBD: ulaw, alaw */
709 	}
710 	return 0;
711 }
712 
713 void
714 rep_write_8_mono(struct repulse_hw *bp, u_int8_t *p, int length,
715 	unsigned flags)
716 {
717 	u_int16_t sample;
718 	u_int16_t xor;
719 
720 	xor = flags & 1 ? 0x8000 : 0;
721 
722 	bp->rhw_fifo_pack = 0;
723 
724 	while (length-- > 0) {
725 		sample = ((*p++) << 8) ^ xor;
726 		bp->rhw_fifo_lh = sample;
727 		bp->rhw_fifo_rh = sample;
728 	}
729 }
730 
731 void
732 rep_write_8_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
733 	unsigned flags)
734 {
735 	u_int16_t xor;
736 
737 	xor = flags & 1 ? 0x8000 : 0;
738 
739 	bp->rhw_fifo_pack = 0;
740 
741 	while (length-- > 0) {
742 		bp->rhw_fifo_lh = ((*p++) << 8) ^ xor;
743 		bp->rhw_fifo_rh = ((*p++) << 8) ^ xor;
744 	}
745 }
746 
747 void
748 rep_write_16_mono(struct repulse_hw *bp, u_int8_t *p, int length,
749 	unsigned flags)
750 {
751 	u_int16_t *q = (u_int16_t *)p;
752 	u_int16_t sample;
753 	u_int16_t xor;
754 
755 	xor = flags & 1 ? 0x8000 : 0;
756 
757 	bp->rhw_fifo_pack = 0;
758 
759 	if (flags & 2) {
760 		while (length > 0) {
761 			sample = bswap16(*q++) ^ xor;
762 			bp->rhw_fifo_lh = sample;
763 			bp->rhw_fifo_rh = sample;
764 			length -= 2;
765 		}
766 		return;
767 	}
768 
769 	while (length > 0) {
770 		sample = (*q++) ^ xor;
771 		bp->rhw_fifo_lh = sample;
772 		bp->rhw_fifo_rh = sample;
773 		length -= 2;
774 	}
775 }
776 
777 void
778 rep_write_16_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
779 	unsigned flags)
780 {
781 	u_int16_t *q = (u_int16_t *)p;
782 	u_int16_t xor;
783 
784 	xor = flags & 1 ? 0x8000 : 0;
785 
786 	bp->rhw_fifo_pack = 0;
787 
788 	if (flags & 2) {
789 		while (length > 0) {
790 			bp->rhw_fifo_lh = bswap16(*q++) ^ xor;
791 			bp->rhw_fifo_rh = bswap16(*q++) ^ xor;
792 			length -= 4;
793 		}
794 		return;
795 	}
796 	while (length > 0) {
797 		bp->rhw_fifo_lh = (*q++) ^ xor;
798 		bp->rhw_fifo_rh = (*q++) ^ xor;
799 		length -= 4;
800 	}
801 }
802 
803 void
804 rep_read_8_mono(struct	repulse_hw  *bp, u_int8_t *p, int length,
805 	unsigned flags)
806 {
807 	u_int16_t v;
808 	u_int16_t xor;
809 
810 	xor = flags & 1 ? 0x8000 : 0;
811 
812 	while (length > 0) {
813 		*p++ = (bp->rhw_fifo_lh ^ xor) >> 8;
814 		v    = bp->rhw_fifo_rh;
815 		length--;
816 	}
817 }
818 
819 void
820 rep_read_16_mono(struct	 repulse_hw  *bp, u_int8_t *p, int length,
821 	unsigned flags)
822 {
823 	u_int16_t *q = (u_int16_t *)p;
824 	u_int16_t v;
825 	u_int16_t xor;
826 
827 	xor = flags & 1 ? 0x8000 : 0;
828 
829 	if (flags & 2) {
830 		while (length > 0) {
831 			*q++ = bswap16(bp->rhw_fifo_lh ^ xor);
832 			v    = bp->rhw_fifo_rh;
833 			length -= 2;
834 		}
835 		return;
836 	}
837 
838 	while (length > 0) {
839 		*q++ = bp->rhw_fifo_lh ^ xor;
840 		v    = bp->rhw_fifo_rh;
841 		length -= 2;
842 	}
843 }
844 
845 void
846 rep_read_8_stereo(struct  repulse_hw  *bp, u_int8_t *p, int length,
847 	unsigned flags)
848 {
849 	u_int16_t xor;
850 
851 	xor = flags & 1 ? 0x8000 : 0;
852 	while (length > 0) {
853 		*p++ = (bp->rhw_fifo_lh ^ xor) >> 8;
854 		*p++ = (bp->rhw_fifo_rh ^ xor) >> 8;
855 		length -= 2;
856 	}
857 }
858 
859 void
860 rep_read_16_stereo(struct  repulse_hw  *bp, u_int8_t *p, int length,
861 	unsigned flags)
862 {
863 	u_int16_t *q = (u_int16_t *)p;
864 	u_int16_t xor;
865 
866 	xor = flags & 1 ? 0x8000 : 0;
867 
868 	if (flags & 2) {
869 		while (length > 0) {
870 			*q++ = bswap16(bp->rhw_fifo_lh ^ xor);
871 			*q++ = bswap16(bp->rhw_fifo_rh ^ xor);
872 			length -= 4;
873 		}
874 		return;
875 	}
876 	while (length > 0) {
877 		*q++ = bp->rhw_fifo_lh ^ xor;
878 		*q++ = bp->rhw_fifo_rh ^ xor;
879 		length -= 4;
880 	}
881 }
882 
883 /*
884  * At this point the transfer function is set.
885  */
886 
887 int
888 rep_start_output(void *addr, void *block, int blksize,
889 	void (*intr)(void*), void *intrarg) {
890 
891 	struct repulse_softc *sc;
892 	u_int8_t *buf;
893 	struct repulse_hw *bp;
894 	u_int16_t status;
895 
896 
897 	sc = addr;
898 	bp = sc->sc_boardp;
899 	buf = block;
900 
901 	/* TODO: prepare hw if necessary */
902 	status = bp->rhw_status;
903 	if (!(status & REPSTATUS_PLAY))
904 		bp->rhw_status = status |
905 		    REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST;
906 
907 	/* copy data */
908 	(*sc->sc_playfun)(bp, buf, blksize, sc->sc_playflags);
909 
910 	/* TODO: set hw if necessary */
911 	if (intr) {
912 		bp->rhw_status |= REPSTATUS_PLAYIRQENABLE;
913 		bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2;
914 		/* /2: give us time to return on the first call */
915 	}
916 
917 	/* save callback function */
918 	sc->sc_playarg = intrarg;
919 	sc->sc_playmore = intr;
920 
921 	return 0;
922 }
923 
924 int
925 rep_start_input(void *addr, void *block, int blksize,
926 	void (*intr)(void*), void *intrarg) {
927 
928 	struct repulse_softc *sc;
929 	struct repulse_hw *bp;
930 	u_int16_t status;
931 
932 	sc = addr;
933 	bp = sc->sc_boardp;
934 
935 	sc->sc_captbuf = block;
936 	sc->sc_captbufsz = blksize;
937 	sc->sc_captarg = intrarg;
938 	sc->sc_captmore = intr;
939 
940 	status = bp->rhw_status;
941 	if (!(status & REPSTATUS_RECORD))
942 		bp->rhw_status = status | REPSTATUS_RECORD
943 			| REPSTATUS_RECFIFORST;
944 
945 	bp->rhw_status |= REPSTATUS_RECIRQENABLE;
946 	bp->rhw_capt_fifosz = blksize / sc->sc_captscale;
947 
948 	return 0;
949 }
950 
951 /* irq handler */
952 
953 int
954 rep_intr(void *tag) {
955 	struct repulse_softc *sc;
956 	struct repulse_hw *bp;
957 	int foundone;
958 	u_int16_t status;
959 
960 	foundone = 0;
961 
962 	sc = tag;
963 	bp = sc->sc_boardp;
964 	status = bp->rhw_status;
965 
966 	if (status & REPSTATUS_PLAYIRQACK) {
967 		foundone = 1;
968 		status &= ~REPSTATUS_PLAYIRQENABLE;
969 		bp->rhw_status = status;
970 		(*sc->sc_playmore)(sc->sc_playarg);
971 	}
972 
973 	if (status & REPSTATUS_RECIRQACK) {
974 		foundone = 1;
975 		status &= ~REPSTATUS_RECIRQENABLE;
976 		bp->rhw_status = status;
977 		(*sc->sc_captfun)(bp, sc->sc_captbuf, sc->sc_captbufsz,
978 			sc->sc_captflags);
979 		(*sc->sc_captmore)(sc->sc_captarg);
980 	}
981 
982 	return foundone;
983 }
984