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