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