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