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