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