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