xref: /netbsd/sys/arch/amiga/dev/repulse.c (revision c4a72b64)
1 /*	$NetBSD: repulse.c,v 1.7 2002/10/08 18:01:42 kent 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.7 2002/10/08 18:01:42 kent 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