repulse.c revision 1.2.4.3 1 /* $NetBSD: repulse.c,v 1.2.4.3 2002/10/18 02:35:03 nathanw 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.2.4.3 2002/10/18 02:35:03 nathanw 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 a = sc->sc_codec_if->vtbl->get_extcaps(sc->sc_codec_if);
339 if (!(a & AC97_EXT_AUDIO_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
346 /*
347 * from auvia.c: disable mutes ...
348 * XXX maybe this should happen in MI code?
349 */
350
351 for (i = 0; i < 5; i++) {
352 static struct {
353 char *class, *device;
354 } d[] = {
355 { AudioCoutputs, AudioNmaster},
356 { AudioCinputs, AudioNdac},
357 { AudioCinputs, AudioNcd},
358 { AudioCinputs, AudioNline},
359 { AudioCrecord, AudioNvolume},
360 };
361
362 ctl.type = AUDIO_MIXER_ENUM;
363 ctl.un.ord = 0;
364 ctl.dev = sc->sc_codec_if->vtbl->get_portnum_by_name(
365 sc->sc_codec_if, d[i].class, d[i].device, AudioNmute);
366 rep_set_port(sc, &ctl);
367 }
368
369 sc->sc_isr.isr_ipl = 2;
370 sc->sc_isr.isr_arg = sc;
371 sc->sc_isr.isr_intr = rep_intr;
372 add_isr(&sc->sc_isr);
373
374 audio_attach_mi(&rep_hw_if, sc, &sc->sc_dev);
375
376 return;
377
378 Initerr:
379 printf("\n%s: firmware not successfully loaded\n", self->dv_xname);
380 return;
381
382 }
383
384 void repac_reset(void *arg) {
385 struct repulse_softc *sc = arg;
386 struct repulse_hw *bp = sc->sc_boardp;
387
388 u_int16_t a;
389
390 a = bp->rhw_status;
391 a |= REPSTATUS_CODECRESET;
392 bp->rhw_status = a;
393
394 a = bp->rhw_status;
395 #ifdef DIAGNOSTIC
396 if ((a & REPSTATUS_CODECRESET) == 0)
397 panic("%s: cannot set reset bit", sc->sc_dev.dv_xname);
398 #endif
399
400 a = bp->rhw_status;
401 a = bp->rhw_status;
402 a = bp->rhw_status;
403 a = bp->rhw_status;
404 a &= ~REPSTATUS_CODECRESET;
405 bp->rhw_status = a;
406 }
407
408 int repac_read(void *arg, u_int8_t reg, u_int16_t *valuep) {
409 struct repulse_softc *sc = arg;
410 struct repulse_hw *bp = sc->sc_boardp;
411
412 while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
413 bp->rhw_reg_address = (reg & 0x7F) | 0x80;
414
415 while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
416
417 *valuep = bp->rhw_reg_data;
418
419 return 0;
420 }
421
422 int repac_write(void *arg, u_int8_t reg, u_int16_t value) {
423 struct repulse_softc *sc = arg;
424 struct repulse_hw *bp = sc->sc_boardp;
425
426 bp->rhw_reg_data = value;
427 bp->rhw_reg_address = reg & 0x7F;
428
429 while (bp->rhw_status & REPSTATUS_REGSENDBUSY);
430
431 return 0;
432 }
433
434 int repac_attach(void *arg, struct ac97_codec_if *acip){
435
436 struct repulse_softc *sc;
437
438 sc = arg;
439 sc->sc_codec_if = acip;
440
441 return 0;
442 }
443
444 /* audio(9) support stuff which is not ac97-constant */
445
446 int
447 rep_open(void *arg, int flags)
448 {
449 return 0;
450 }
451
452 void
453 rep_close(void *arg)
454 {
455 struct repulse_softc *sc = arg;
456
457 rep_halt_output(sc);
458 rep_halt_input(sc);
459 }
460
461 int
462 rep_getdev(void *arg, struct audio_device *retp)
463 {
464 struct repulse_softc *sc = arg;
465 struct repulse_hw *bp = sc->sc_boardp;
466
467 if (retp) {
468 strncpy(retp->name, "Repulse", sizeof(retp->name));
469 snprintf(retp->version, sizeof(retp->version), "0x%x",
470 bp->rhw_version);
471 strncpy(retp->config, "", sizeof(retp->config));
472 }
473
474 return 0;
475 }
476
477 int
478 rep_get_props(void *v)
479 {
480 return (AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX);
481 }
482
483 int
484 rep_halt_output(void *arg)
485 {
486 struct repulse_softc *sc = arg;
487 struct repulse_hw *bp = sc->sc_boardp;
488
489 bp->rhw_status &= ~(REPSTATUS_PLAYIRQENABLE|REPSTATUS_PLAY);
490
491
492 return 0;
493 }
494
495 int
496 rep_halt_input(void *arg)
497 {
498 struct repulse_softc *sc = arg;
499 struct repulse_hw *bp = sc->sc_boardp;
500
501 bp->rhw_status &= ~(REPSTATUS_RECIRQENABLE|REPSTATUS_RECORD);
502
503 return 0;
504 }
505
506 /*
507 * Encoding support.
508 *
509 * TODO: add 24bit and 32bit modes here and in setparams.
510 */
511
512 const struct repulse_encoding_query {
513 const char *name;
514 int encoding, precision, flags;
515 } rep_encoding_queries[] = {
516 { AudioEulinear, AUDIO_ENCODING_ULINEAR, 8, 0},
517 { AudioEmulaw, AUDIO_ENCODING_ULAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
518 { AudioEalaw, AUDIO_ENCODING_ALAW, 8, AUDIO_ENCODINGFLAG_EMULATED},
519 { AudioEslinear, AUDIO_ENCODING_SLINEAR, 8, 0},
520 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16, 0},
521 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE, 16, 0},
522 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE, 16, 0},
523 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16, 0},
524 };
525
526 int
527 rep_query_encoding(void *arg, struct audio_encoding *fp)
528 {
529 int i;
530 const struct repulse_encoding_query *p;
531
532 i = fp->index;
533
534 if (i >= sizeof(rep_encoding_queries) /
535 sizeof(struct repulse_encoding_query))
536 return (EINVAL);
537
538 p = &rep_encoding_queries[i];
539
540 strncpy (fp->name, p->name, sizeof(fp->name));
541 fp->encoding = p->encoding;
542 fp->precision = p->precision;
543 fp->flags = p->flags;
544
545 return (0);
546 }
547
548 /*
549 * XXX the following three functions need to be enhanced for the FPGA s/pdif
550 * mode. Generic ac97 versions for now.
551 */
552
553 int
554 rep_get_port(void *arg, mixer_ctrl_t *cp)
555 {
556 struct repulse_softc *sc = arg;
557
558 return (sc->sc_codec_if->vtbl->mixer_get_port(sc->sc_codec_if, cp));
559 }
560
561 int
562 rep_set_port(void *arg, mixer_ctrl_t *cp)
563 {
564 struct repulse_softc *sc = arg;
565
566 return (sc->sc_codec_if->vtbl->mixer_set_port(sc->sc_codec_if, cp));
567 }
568
569 int
570 rep_query_devinfo (void *arg, mixer_devinfo_t *dip)
571 {
572 struct repulse_softc *sc = arg;
573
574 return (sc->sc_codec_if->vtbl->query_devinfo(sc->sc_codec_if, dip));
575 }
576
577 int
578 rep_round_blocksize(void *arg, int blk)
579 {
580 int b1;
581
582 b1 = (blk & -32);
583
584 if (b1 > 65536 / 2 / 2 /* channels */ / 4 /* bytes per channel */)
585 b1 = 65536 / 2 / 2 / 4;
586 return (b1);
587 }
588
589 size_t
590 rep_round_buffersize(void *arg, int direction, size_t size)
591 {
592 return size;
593 }
594
595
596 int
597 rep_set_params(void *addr, int setmode, int usemode,
598 struct audio_params *play, struct audio_params *rec)
599 {
600 struct repulse_softc *sc = addr;
601 struct audio_params *p;
602 int mode, reg;
603 unsigned flags;
604 u_int16_t a;
605
606 /* for mode in (RECORD, PLAY) */
607 for (mode = AUMODE_RECORD; mode != -1;
608 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
609
610 if ((setmode & mode) == 0)
611 continue;
612
613 p = mode == AUMODE_PLAY ? play : rec;
614
615 /* TODO XXX we can do upto 32bit, 96000 */
616 if (p->sample_rate < 4000 || p->sample_rate > 48000 ||
617 (p->precision != 8 && p->precision != 16) ||
618 (p->channels != 1 && p->channels != 2))
619 return (EINVAL);
620
621 reg = mode == AUMODE_PLAY ?
622 AC97_REG_PCM_FRONT_DAC_RATE : AC97_REG_PCM_LR_ADC_RATE;
623
624 repac_write(sc, reg, (u_int16_t) p->sample_rate);
625 repac_read(sc, reg, &a);
626 p->sample_rate = a;
627
628 if (mode == AUMODE_PLAY)
629 sc->sc_playscale = p->channels * p->precision / 8;
630 else
631 sc->sc_captscale = p->channels * p->precision / 8;
632
633 p->factor = 1;
634 p->sw_code = 0;
635
636 /* everything else is software, alas... */
637 /* XXX TBD signed/unsigned, *law, etc */
638
639 flags = 0;
640 if (p->encoding == AUDIO_ENCODING_ULINEAR_LE ||
641 p->encoding == AUDIO_ENCODING_ULINEAR_BE ||
642 p->encoding == AUDIO_ENCODING_ULINEAR)
643 flags |= 1;
644
645 if (p->encoding == AUDIO_ENCODING_SLINEAR_LE ||
646 p->encoding == AUDIO_ENCODING_ULINEAR_LE)
647 flags |= 2;
648
649 if (mode == AUMODE_PLAY) {
650 sc->sc_playflags = flags;
651 if (p->encoding == AUDIO_ENCODING_ULAW) {
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 p->sw_code = mulaw_to_slinear16_be;
658 p->factor = 2;
659 } else
660 if (p->encoding == AUDIO_ENCODING_ALAW) {
661 sc->sc_playfun = p->channels == 1 ?
662 rep_write_16_mono :
663 rep_write_16_stereo;
664 sc->sc_playflags = 0;
665 sc->sc_playscale = p->channels * 2;
666 p->sw_code = alaw_to_slinear16_be;
667 p->factor = 2;
668 } else
669 if (p->precision == 8 && p->channels == 1)
670 sc->sc_playfun = rep_write_8_mono;
671 else if (p->precision == 8 && p->channels == 2)
672 sc->sc_playfun = rep_write_8_stereo;
673 else if (p->precision == 16 && p->channels == 1)
674 sc->sc_playfun = rep_write_16_mono;
675 else if (p->precision == 16 && p->channels == 2)
676 sc->sc_playfun = rep_write_16_stereo;
677 } else {
678 sc->sc_captflags = flags;
679 if (p->encoding == AUDIO_ENCODING_ULAW) {
680 sc->sc_captfun = p->channels == 1 ?
681 rep_read_8_mono :
682 rep_read_8_stereo;
683 sc->sc_captflags = 0;
684 p->sw_code = slinear8_to_mulaw;
685 p->factor = 1;
686 } else
687 if (p->encoding == AUDIO_ENCODING_ALAW) {
688 sc->sc_captfun = p->channels == 1 ?
689 rep_read_8_mono :
690 rep_read_8_stereo;
691 sc->sc_captflags = 0;
692 p->sw_code = slinear8_to_alaw;
693 p->factor = 1;
694 } else
695 if (p->precision == 8 && p->channels == 1)
696 sc->sc_captfun = rep_read_8_mono;
697 else if (p->precision == 8 && p->channels == 2)
698 sc->sc_captfun = rep_read_8_stereo;
699 else if (p->precision == 16 && p->channels == 1)
700 sc->sc_captfun = rep_read_16_mono;
701 else if (p->precision == 16 && p->channels == 2)
702 sc->sc_captfun = rep_read_16_stereo;
703 }
704 /* TBD: ulaw, alaw */
705 }
706 return 0;
707 }
708
709 void
710 rep_write_8_mono(struct repulse_hw *bp, u_int8_t *p, int length,
711 unsigned flags)
712 {
713 u_int16_t sample;
714 u_int16_t xor;
715
716 xor = flags & 1 ? 0x8000 : 0;
717
718 bp->rhw_fifo_pack = 0;
719
720 while (length-- > 0) {
721 sample = ((*p++) << 8) ^ xor;
722 bp->rhw_fifo_lh = sample;
723 bp->rhw_fifo_rh = sample;
724 }
725 }
726
727 void
728 rep_write_8_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
729 unsigned flags)
730 {
731 u_int16_t xor;
732
733 xor = flags & 1 ? 0x8000 : 0;
734
735 bp->rhw_fifo_pack = 0;
736
737 while (length-- > 0) {
738 bp->rhw_fifo_lh = ((*p++) << 8) ^ xor;
739 bp->rhw_fifo_rh = ((*p++) << 8) ^ xor;
740 }
741 }
742
743 void
744 rep_write_16_mono(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 sample;
749 u_int16_t xor;
750
751 xor = flags & 1 ? 0x8000 : 0;
752
753 bp->rhw_fifo_pack = 0;
754
755 if (flags & 2) {
756 while (length > 0) {
757 sample = bswap16(*q++) ^ xor;
758 bp->rhw_fifo_lh = sample;
759 bp->rhw_fifo_rh = sample;
760 length -= 2;
761 }
762 return;
763 }
764
765 while (length > 0) {
766 sample = (*q++) ^ xor;
767 bp->rhw_fifo_lh = sample;
768 bp->rhw_fifo_rh = sample;
769 length -= 2;
770 }
771 }
772
773 void
774 rep_write_16_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
775 unsigned flags)
776 {
777 u_int16_t *q = (u_int16_t *)p;
778 u_int16_t xor;
779
780 xor = flags & 1 ? 0x8000 : 0;
781
782 bp->rhw_fifo_pack = 0;
783
784 if (flags & 2) {
785 while (length > 0) {
786 bp->rhw_fifo_lh = bswap16(*q++) ^ xor;
787 bp->rhw_fifo_rh = bswap16(*q++) ^ xor;
788 length -= 4;
789 }
790 return;
791 }
792 while (length > 0) {
793 bp->rhw_fifo_lh = (*q++) ^ xor;
794 bp->rhw_fifo_rh = (*q++) ^ xor;
795 length -= 4;
796 }
797 }
798
799 void
800 rep_read_8_mono(struct repulse_hw *bp, u_int8_t *p, int length,
801 unsigned flags)
802 {
803 u_int16_t v;
804 u_int16_t xor;
805
806 xor = flags & 1 ? 0x8000 : 0;
807
808 while (length > 0) {
809 *p++ = (bp->rhw_fifo_lh ^ xor) >> 8;
810 v = bp->rhw_fifo_rh;
811 length--;
812 }
813 }
814
815 void
816 rep_read_16_mono(struct repulse_hw *bp, u_int8_t *p, int length,
817 unsigned flags)
818 {
819 u_int16_t *q = (u_int16_t *)p;
820 u_int16_t v;
821 u_int16_t xor;
822
823 xor = flags & 1 ? 0x8000 : 0;
824
825 if (flags & 2) {
826 while (length > 0) {
827 *q++ = bswap16(bp->rhw_fifo_lh ^ xor);
828 v = bp->rhw_fifo_rh;
829 length -= 2;
830 }
831 return;
832 }
833
834 while (length > 0) {
835 *q++ = bp->rhw_fifo_lh ^ xor;
836 v = bp->rhw_fifo_rh;
837 length -= 2;
838 }
839 }
840
841 void
842 rep_read_8_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
843 unsigned flags)
844 {
845 u_int16_t xor;
846
847 xor = flags & 1 ? 0x8000 : 0;
848 while (length > 0) {
849 *p++ = (bp->rhw_fifo_lh ^ xor) >> 8;
850 *p++ = (bp->rhw_fifo_rh ^ xor) >> 8;
851 length -= 2;
852 }
853 }
854
855 void
856 rep_read_16_stereo(struct repulse_hw *bp, u_int8_t *p, int length,
857 unsigned flags)
858 {
859 u_int16_t *q = (u_int16_t *)p;
860 u_int16_t xor;
861
862 xor = flags & 1 ? 0x8000 : 0;
863
864 if (flags & 2) {
865 while (length > 0) {
866 *q++ = bswap16(bp->rhw_fifo_lh ^ xor);
867 *q++ = bswap16(bp->rhw_fifo_rh ^ xor);
868 length -= 4;
869 }
870 return;
871 }
872 while (length > 0) {
873 *q++ = bp->rhw_fifo_lh ^ xor;
874 *q++ = bp->rhw_fifo_rh ^ xor;
875 length -= 4;
876 }
877 }
878
879 /*
880 * At this point the transfer function is set.
881 */
882
883 int
884 rep_start_output(void *addr, void *block, int blksize,
885 void (*intr)(void*), void *intrarg) {
886
887 struct repulse_softc *sc;
888 u_int8_t *buf;
889 struct repulse_hw *bp;
890 u_int16_t status;
891
892
893 sc = addr;
894 bp = sc->sc_boardp;
895 buf = block;
896
897 /* TODO: prepare hw if necessary */
898 status = bp->rhw_status;
899 if (!(status & REPSTATUS_PLAY))
900 bp->rhw_status = status |
901 REPSTATUS_PLAY | REPSTATUS_PLAYFIFORST;
902
903 /* copy data */
904 (*sc->sc_playfun)(bp, buf, blksize, sc->sc_playflags);
905
906 /* TODO: set hw if necessary */
907 if (intr) {
908 bp->rhw_status |= REPSTATUS_PLAYIRQENABLE;
909 bp->rhw_play_fifosz = blksize / sc->sc_playscale / 2;
910 /* /2: give us time to return on the first call */
911 }
912
913 /* save callback function */
914 sc->sc_playarg = intrarg;
915 sc->sc_playmore = intr;
916
917 return 0;
918 }
919
920 int
921 rep_start_input(void *addr, void *block, int blksize,
922 void (*intr)(void*), void *intrarg) {
923
924 struct repulse_softc *sc;
925 struct repulse_hw *bp;
926 u_int16_t status;
927
928 sc = addr;
929 bp = sc->sc_boardp;
930
931 sc->sc_captbuf = block;
932 sc->sc_captbufsz = blksize;
933 sc->sc_captarg = intrarg;
934 sc->sc_captmore = intr;
935
936 status = bp->rhw_status;
937 if (!(status & REPSTATUS_RECORD))
938 bp->rhw_status = status | REPSTATUS_RECORD
939 | REPSTATUS_RECFIFORST;
940
941 bp->rhw_status |= REPSTATUS_RECIRQENABLE;
942 bp->rhw_capt_fifosz = blksize / sc->sc_captscale;
943
944 return 0;
945 }
946
947 /* irq handler */
948
949 int
950 rep_intr(void *tag) {
951 struct repulse_softc *sc;
952 struct repulse_hw *bp;
953 int foundone;
954 u_int16_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