xref: /netbsd-src/sys/arch/amiga/dev/repulse.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
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