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